32 Commits

Author SHA1 Message Date
Minoricew
b8f27d9f7c [ Feat / 🛠️ Fix] Auto hide desktopAssistant & Fix #26
1. [/] 修改了 Header Icon 的隐藏方式, 尝试修复 #26
2. [+] 现在可以隐藏管家助手 (桌面右下角小窗) 了
3. [⇡] Bump version to v0.1.1-beta
2025-06-20 00:45:11 +08:00
Minoricew
807ac913da [🤯 Refactor] Use standalone window for WebSocket connections
1. [-] 删除了对于 desktopAssistant 的 Hook 逻辑
2. [/] 现在不再使用 desktopAssistant 保活 WS 连接, 而是用一个单独的不可见窗口处理 WS (#25)
2025-06-17 18:14:58 +08:00
Minoricew
f6a30351fd [🛠️ Fix] Change the logic of determining isEncConfig
1. [/] 修正了识别配置文件加密状态时的逻辑错误
2025-06-17 00:50:54 +08:00
Minoricew
0494394fd8 [🛠️ Fix] Emergency fix for invalid config read logic
1. [/] 紧急修复了上个 commit 中引入的错误路径获取逻辑
2025-06-17 00:16:43 +08:00
Minoricew
6be31652f9 [🛠️ Fix] 不好意思脑子抽了 2025-06-16 21:41:07 +08:00
Minoricew
a638a29cc6 [🛠️ Fix] Invalid log dir path (#24) & Remove PLS trust token
1. [-] 删除了 PLS 的 Trust token 认证机制
2. [+] 现在可以在 `偏好设置` - `调试选项` 中直接打开日志文件夹了
3. [/] 日志目录不再使用 `%USERPROFILE%\Documents\HugoAura\logs` 为基准, 而是从注册表获取值
4. [/] 配置文件目录同理, 旧版配置文件将会自动迁移到新位置
2025-06-16 21:32:15 +08:00
Minoricew
dae0f033a5 [🔁 Chore] Add forum links into README & issue template 2025-06-16 01:02:40 +08:00
Minoricew
bd2f2e4154 [ Feat] Add AppBar buttons pos customize options
1. [+] 允许用户自定义应用栏上操作类按钮的位置
2025-06-14 18:44:34 +08:00
Minoricew
290cbfed53 [🔁 Chore] Auto upload release for workflow 2025-06-14 17:01:51 +08:00
Minoricew
9a2a335742 [ Feat] {#21} Add AppBar window oper icons
1. [+] {#21} 增加了应用栏 (顶栏) 上的窗口操作按钮
2. [-] 删掉了几个调试时留下的 console.debug
2025-06-14 16:07:10 +08:00
Minoricew
e63c989d88 [🚧 Fix] Infrastructure logic errors
1. [/] 修复了 Header Icon 默认样式为 Hidden, 导致 UI Loader 尝试 Revive 后 Header Icon 消失的异常。
2. [/] 修正了一个 PLS 连接逻辑错误 (未正确处理更新 `installed` 参数的时机)。
3. [/] 修正了冰点上报拦截预览视图中, 未正确判断状态码导致的异常。
2025-06-13 22:26:36 +08:00
Minoricew
70ffa3f581 [ Feat] <Settings UI> Support custom access methods
1. [/] 修复了 PLS 下载时, 目录未递归创建导致的 ENOENT 错误。
2. [+] 现在可以通过多种方式访问 Aura 设置 UI 了, 更多详细信息, 请参见 #18。
3. [↑] 优化了 Tooltip 的渲染逻辑。
2025-06-13 16:24:10 +08:00
Minoricew
c0249693a8 [🚧 Fix] <Settings> Invalid (outdated) tip msg in pref/aura
1. [-] 删除了来自旧版本的 Tip (现在配置文件已经可以手动启 / 禁用加密了)
2025-06-13 12:12:46 +08:00
Minoricew
ca5d94ebd8 [🚧 Fix] <PLS & FS> Improve PLS download logic & UX
1. [+] 增加了 PLS 下载操作的取消功能
2. [/] 修复了 FS IPC 中 `downloadFile` 时, 过早地从 downloadTasks 中删除任务的逻辑错误。
3. [↑] 改进了 PLS IPC 中 `handlePLSDownload` 获取版本信息时的逻辑, 现在该函数会从全局 API 信息中逐个尝试 API 域名。减少了极端网络环境下, 版本信息获取失败的可能性。
4. [/] 修复了下载失败后, 下载按钮依然保持灰显的问题。
5. [+] 为 PLS 下载增加了进度条显示。
6. [/] 优化了 `plsConnectionManager` 中一些不必要的 IPC 状态同步 (有些时候还会导致逻辑错误)。
2025-06-13 11:49:22 +08:00
Minoricew
a9d3772b51 [Fix] <PLS> More verbose logging & Change API domain 2025-06-12 20:30:13 +08:00
Minoricew
9e63a9374f [Feat] Log file auto cleanup (#15) & Improve logger experience
Co-authored-by: TianMiao <tianmiao.work@foxmail.com>
2025-06-11 17:59:57 +08:00
Minoricew
c3a70ece88 [Fix] IPC plsStats not sync 2025-06-11 17:29:28 +08:00
Minoricew
54df7011c0 [Fix] Page crash when plsRules === null 2025-06-11 01:10:44 +08:00
Minoricew
423142d785 [Fix] Minor UX tweaks: freezeOverride components 2025-06-11 00:35:49 +08:00
Minoricew
a639ae0ac6 [Feat] Refactor UI loader & Add freeze report override 2025-06-10 00:28:53 +08:00
Minoricew
ed29f1b86f [Feat] Download PLS from API 2025-06-09 18:02:11 +08:00
Minoricew
8ab55bc93c [Feat] Further PLS ctrl & childProc perf improvements 2025-06-07 23:51:54 +08:00
Minoricew
50bde49414 [Chore] Create auto pack workflow 2025-06-06 19:07:49 +08:00
Minoricew
7b7254f1bb [Chore] Add Repobeats analytics img 2025-06-06 17:57:32 +08:00
Minoricew
6da8348b41 [Feat] Basic PLS lifecycle mgmt 2025-06-06 02:05:43 +08:00
Minoricew
839afa79e8 [Chore] Update README 2025-06-05 00:42:13 +08:00
Minoricew
7c8d3d4fbc [Feat] New settings passwd UX & Config enc support 2025-06-05 00:35:50 +08:00
Minoricew
fbc5cf1f57 [Feat] Screen Lock overrides & Move Aura Settings to pref page 2025-06-03 02:11:39 +08:00
Minoricew
a86d13431b [Feat] Settings UI Auth (#3) & Prepare for v0.1.1-rel 2025-06-02 00:23:00 +08:00
Minoricew
1320f5397a [Chore] Minor README update 2025-05-28 18:50:07 +08:00
Minoricew
fece9a8805 [Chore] Add Issue templates 2025-05-26 00:00:58 +08:00
Minoricew
12f1040884 [Feat] Early support for PLS & Use JSDoc (Partially) 2025-05-25 22:43:01 +08:00
114 changed files with 10428 additions and 945 deletions

67
.github/ISSUE_TEMPLATE/bugReport.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: 反馈异常 / Bug Report
description: 反馈应用中的问题, 或预期外的行为
labels: ["Bug"]
title: "[Bug] "
body:
- type: textarea
attributes:
label: 复现步骤 / Reproduce steps
placeholder: |
1. 首先, 我...
2. 然后, 出现了...
3. 最终...
validations:
required: true
- type: textarea
attributes:
label: 预期行为 / Expected behaviour
placeholder: |
1. 应用本来应该...
validations:
required: true
- type: textarea
attributes:
label: 实际行为 / Actual behaviour
placeholder: |
1. 事实上, 应用...
validations:
required: true
- type: input
attributes:
label: HugoAura 版本 / HugoAura version
validations:
required: true
- type: input
attributes:
label: 希沃管家版本 / Seewo Services version
validations:
required: true
- type: checkboxes
id: confirms
attributes:
label: 提交前请确认 / Confirm before submit
options:
- label: 我已经在 [HugoAura Issues](https://github.com/HugoAura/Seewo-HugoAura/issues) 中查询过相关关键词, 并确定此 Issue 是唯一的。
required: true
- label: 我已经确认, 我运行的 HugoAura 版本与希沃管家客户端版本相兼容
required: true
- type: textarea
attributes:
label: 日志 / Logs
description: 请前往 `用户文件夹/Documents/HugoAura/logs` 取得日志
placeholder: |
[LOG] Logger initialized. Log file: C:\...
[LOG] [HugoAura / Loaded] Aura is loaded!
validations:
required: false
- type: textarea
id: extraDesc
attributes:
label: 其他信息 (可选) / Additional info (Optional)

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: false

View File

@@ -0,0 +1,53 @@
name: 功能请求 / Feature Request
description: 提交你想要的新功能
labels: ["Feature"]
title: "[Feature Request] "
body:
- type: textarea
attributes:
label: 概述 / Overview
description: 简述你想要的新功能, 需要它做什么
placeholder:
validations:
required: true
- type: textarea
attributes:
label: 背景 (可选) / Background info (Optional)
description: 有哪些因素让你想到了这个新功能? 它为什么对用户体验至关重要?
placeholder:
validations:
required: false
- type: textarea
attributes:
label: 具体需求 / Detail Requirements
description: 请详细描述具体希望实现哪些目标, 以及提出你认为可能的实现思路
placeholder: |
我希望能够实现...
具体来讲, 包含:
1. 实现 XXX
2. XXXXXX
3. XXXXXX
validations:
required: true
- type: input
attributes:
label: HugoAura 版本 / HugoAura version
description: 创建此 Issue 时的最新 HugoAura 版本
validations:
required: true
- type: checkboxes
id: confirms
attributes:
label: 提交前请确认 / Confirm before submit
options:
- label: 我已经在 [HugoAura Issues](https://github.com/HugoAura/Seewo-HugoAura/issues) 中查询过相关关键词, 并确定此功能请求是唯一的。
required: true
- type: textarea
id: extraDesc
attributes:
label: 其他信息 (可选) / Additional info (Optional)

20
.github/ISSUE_TEMPLATE/other.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: 其他问题 / Any Other
description: 其他任何类型的问题
labels: []
title: "[Question] "
body:
- type: textarea
attributes:
label: 你的问题 / Your question
placeholder: |
开发者能不能 v 我 50
validations:
required: true
- type: checkboxes
id: confirms
attributes:
label: 提交前请确认 / Confirm before submit
options:
- label: 请勿利用此 Issue 类型进行闲聊 / 灌水, 如需闲聊, 可移步至 HugoAura 社区 (forum.aurax.cc)
required: true

178
.github/workflows/pack.yml vendored Normal file
View File

@@ -0,0 +1,178 @@
name: Patch ASAR & Pack source code
on:
push:
branches: [dev, stable]
pull_request:
branches: [dev, stable, main, master]
workflow_dispatch:
permissions: write-all
jobs:
pack:
name: Patch & Pack
runs-on: ubuntu-latest
steps:
- name: Setup Node.JS 20 LTS
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y p7zip-full wget
npm install -g asar
- name: Checkout repository code
uses: actions/checkout@v4
with:
path: "./HugoAura-Code"
- name: Download SeewoServiceSetup
run: |
echo "[DEBUG] Working DIR:"
pwd
wget -nv -O "SeewoService.exe" "https://e.seewo.com/download/file?code=SeewoServiceSetup"
echo "[DEBUG] Listing downloaded file"
ls -la SeewoService*
- name: Extract SeewoServiceSetup as zip
run: |
mkdir -p SeewoServiceSource
7z x "SeewoService.exe" -o"./SeewoServiceSource/" -y
echo "[DEBUG] Listing extracted files"
ls -la SeewoServiceSource/
- name: Extract ASAR file
run: |
cd SeewoServiceSource
SEEWO_DIR=$(find . -name "SeewoService_*" -type d | head -1)
echo "[INFO] Found SeewoService directory: $SEEWO_DIR"
cd "$SEEWO_DIR/SeewoServiceAssistant/resources/"
asar extract ./app.asar ./app-unpacked
- name: Move app-unpacked and clean SSASource
run: |
mkdir -p SSARes
cd SeewoServiceSource
SEEWO_DIR=$(find . -name "SeewoService_*" -type d | head -1)
mv "$SEEWO_DIR/SeewoServiceAssistant/resources/app-unpacked" "../SSARes/"
cd ..
rm -rf SeewoServiceSource
rm -rf *.exe
echo "[DEBUG] Listing Working DIR"
ls -la
- name: Patch main.js
run: |
cd SSARes/app-unpacked
### cp main.js main.js.bak
cat > patch_main.js << 'EOF'
const fs = require('fs');
let content = fs.readFileSync('main.js', 'utf8');
content = 'const hook = require("./hook.js");\n' + content;
content = content.replace(/n\.m=e/, ';const zeron = require("./zeron.js");n = zeron(n);n.m=e');
content = content.replace(/let f=new s\(Object\.assign\(\{\},\{transparent:!0,/, ';hook({ central: n, windowName: this.wname, config: c });let f=new s(Object.assign({},{transparent:!0,');
content = content.replace(/c\.canOpenDevTool/, 'c.canOpenDevTool,preload: __dirname + "\\\\preload.js"');
fs.writeFileSync('main.js', content);
console.log('[SUCCESS] main.js has been modified successfully');
EOF
echo "[DEBUG] Running patch_main.js"
node ./patch_main.js
rm -rf patch_main.js
- name: Copy HugoAura core files
run: |
cp -r HugoAura-Code/src/core/* SSARes/app-unpacked/
echo "[DEBUG] Listing files under <Working DIR>/SSARes/app-unpacked/"
ls -la SSARes/app-unpacked/
- name: Create artifacts DIR and re-pack ASAR
run: |
echo "[DEBUG] Creating artifacts directory"
mkdir -p Artifacts
asar pack SSARes/app-unpacked Artifacts/app-patched.asar
- name: Create aura.zip
run: |
cd HugoAura-Code/src/aura
zip -r ../../../Artifacts/aura.zip .
cd ../../..
echo "[DEBUG] Files in <Working DIR>/Artifacts directory:"
ls -la Artifacts/
- name: Get commit hash & build time
run: |
cd ./HugoAura-Code
SHORT_HASH=$(git rev-parse --short=7 HEAD)
FULL_HASH=$(git rev-parse HEAD)
echo "SHORT_HASH=$SHORT_HASH" >> $GITHUB_ENV
echo "FULL_HASH=$FULL_HASH" >> $GITHUB_ENV
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
- name: Create release content
run: |
cat > rel_msg.txt << 'EOF'
## 这是 HugoAura 的 CI 自动构建版本
### 版本类型: 🔁 自动构建版
### 版本号: `vAutoBuild-${{ env.SHORT_HASH }}`
### 对应 Commit: [`${{ env.SHORT_HASH }}`](https://github.com/HugoAura/Seewo-HugoAura/commit/${{ env.FULL_HASH }})
---
### ⚠ 注意: CI 自动构建版本可能不稳定 / 存在较多 Bug, 更新时请留意
#### 🕘 构建时间: ${{ env.BUILDTIME }}
EOF
- name: Upload patched ASAR
uses: actions/upload-artifact@v4
with:
name: asar-patched
path: Artifacts/app-patched.asar
- name: Upload aura.zip
uses: actions/upload-artifact@v4
with:
name: aura-code
path: Artifacts/aura.zip
- name: Upload release
uses: softprops/action-gh-release@v2
with:
tag_name: vAutoBuild
name: "[CI] HugoAura Auto Build Release"
body_path: rel_msg.txt
prerelease: true
generate_release_notes: false
token: ${{ secrets.GITHUB_TOKEN }}
files: |
Artifacts/app-patched.asar
Artifacts/aura.zip

53
.gitignore vendored Normal file → Executable file
View File

@@ -1,29 +1,24 @@
# Dependencies
node_modules/
.npm
.yarn
# ESLint Cache
.eslintcache
# NYC Test Output
.nyc_output
# Build Artifacts
/dist
/buikd
# dotEnv Files
.env
.env*
# Misc
.DS_Store
*.log
*.bak
# SSA Defaults
_extraResources
addons/
app.asar/addons
app.asar/assets
app.asar/config
app.asar/node_modules
app.asar/public
app.asar/main.js
app.asar/package.json
assets/
autoConfiguration.json
# NPM Packages
node_modules/
# OS Files
desktop.ini
.DS_Store
# Editor Files
.vscode/

0
LICENSE Normal file → Executable file
View File

11
README.md Normal file → Executable file
View File

@@ -1,18 +1,21 @@
<center><img src="https://s2.loli.net/2025/04/18/IpZL7qMw2KFEi8o.png" /></center>
<h1 align="center">HugoAura</h1>
<h4 align="center">下一代希沃管家注入式修改方案</h4>
<div align="center">
<a href="https://github.com/HugoAura/HugoAura">首页</a> · <a href="https://hugoaura.org/about">关于</a> · <a href="https://github.com/HugoAura/HugoAura/wiki">文档</a> · <a href="https://github.com/HugoAura/HugoAura/issues">反馈</a>
<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>
</div>
<br />
> [!TIP]
> HugoAura 的首个预览版已发布! [查看安装教程](https://github.com/HugoAura/HugoAura/wiki)
> [!IMPORTANT]
> 已经过测试的希沃管家版本: v1.5.4.3824
![Repobeats](https://repobeats.axiom.co/api/embed/69b5be5daacef624b8f5e4b8966a0b5898439a22.svg "Repobeats analytics image")
## ✨ 概览
[天下](https://www.bilibili.com/video/BV1UN4y1k7bA) [苦希沃管家](https://www.bilibili.com/video/BV18Z421j7Lf) [久矣](https://github.com/255doesnotexist/SeewoAssistantPasswordRecovery), 如此~~好用~~的一款集控软件, 让广大电教委员对它~~爱不释手~~。
@@ -29,8 +32,8 @@
- [x] 修改希沃管家密码认证组件 (自定义密码 / 解除密码 / 重设认证方式 / ...)
- [x] 阻止希沃管家前端 Audit 上报行为
- [x] 屏蔽屏幕锁 / 自定义屏幕锁行为
- [ ] Aura 代理层服务 (篡改上报数据 / 欺骗冰冻状态)
- [ ] 屏蔽屏幕锁 / 自定义屏幕锁行为
- [ ] 窥屏提醒
- [ ] 插件功能

9
jsconfig.json Executable file
View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"module": "CommonJS",
"target": "ES2015",
"allowImportingTsExtensions": false,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}

21
package.json Executable file
View File

@@ -0,0 +1,21 @@
{
"name": "HugoAura",
"version": "0.1.1-beta",
"description": "Aura for SeewoHugo",
"main": "app.asar/main.js",
"dependencies": {},
"devDependencies": {
"electron": "^36.3.2"
},
"scripts": {},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/HugoAura/Seewo-HugoAura.git"
},
"author": "Minoricew",
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/HugoAura/Seewo-HugoAura/issues"
},
"homepage": "https://github.com/HugoAura/Seewo-HugoAura"
}

0
scripts/cd.bat Normal file → Executable file
View File

0
scripts/kad.bat Normal file → Executable file
View File

0
scripts/kar.bat Normal file → Executable file
View File

89
src/aura/init/main/ipcHandler.js Normal file → Executable file
View File

@@ -1,10 +1,97 @@
// @ts-check
const __SCOPE = "main";
/**
*
* @param {import("electron")} electron
*/
const buildIpcMain = (electron) => {
const { app, ipcMain } = electron;
const { app } = electron;
/**
* @type {import("../../types/main/electron").AuraIPCMain}
*/
// @ts-expect-error
const ipcMain = electron.ipcMain;
/**
*
* @param {string} windowKey
* @param {string} channel
* @param {any} data
* @param {import("electron").WebContents?} grep
*/
ipcMain.send = (windowKey, channel, data, grep = null) => {
/**
*
* @param {string} key
* @param {string} chan
* @param {any} targetData
*/
if (!global.__HUGO_AURA__.hookedWindows) {
return {
success: false,
};
}
const sendDataToWebContents = (key, chan, targetData) => {
const webContents =
// @ts-expect-error
global.__HUGO_AURA__.hookedWindows.get(key)?.webContents;
if (!webContents) {
console.error(
`[HugoAura / Main / IPC / ERROR] Failed sending data to ${key}: WebContents not found`
);
return {
success: false,
};
}
if (grep !== webContents) {
webContents.send(chan, targetData);
}
return {
success: true,
};
};
if (windowKey === "*") {
for (const perWindow of global.__HUGO_AURA__.hookedWindows.keys()) {
sendDataToWebContents(perWindow, channel, data);
}
} else {
const isWindowValid = global.__HUGO_AURA__.hookedWindows.has(windowKey);
if (!isWindowValid) {
console.warn(
`[HugoAura / Main / IPC / WARN] Unknown windowKey: ${windowKey}, window may not have started yet.`
);
return {
success: false,
};
}
sendDataToWebContents(windowKey, channel, data);
}
};
const { applyBaseIpcHandler } = require("./ipcModules/baseIpcHandler");
const { applyDebugIpcHandler } = require("./ipcModules/debugIpcHandler");
const { applyConfigIpcHandler } = require("./ipcModules/configIpcHandler");
const { applyFsIpcHandler } = require("./ipcModules/fsIpcHandler");
const { applyPlsIpcHandler } = require("./ipcModules/plsIpcHandler");
ipcMain.handle("$aura.base.restartApplication", async () => {
app.relaunch();
app.exit(0);
});
applyBaseIpcHandler(ipcMain);
applyDebugIpcHandler(ipcMain);
applyConfigIpcHandler(ipcMain);
applyFsIpcHandler(ipcMain);
applyPlsIpcHandler(ipcMain);
};
module.exports = { buildIpcMain };

View File

@@ -0,0 +1,73 @@
// @ts-check
const { BrowserWindow } = require("electron");
const composables = {
getBrowserWindowInstance: (windowKey) => {
if (!global.__HUGO_AURA__.hookedWindows) return null;
const hookedWindowIns = global.__HUGO_AURA__.hookedWindows.get(windowKey);
if (!hookedWindowIns) return undefined;
const browserWindowIns = BrowserWindow.fromWebContents(
hookedWindowIns.webContents
);
return browserWindowIns;
},
};
/**
*
* @param {import("electron").IpcMain} ipcMain
*/
const applyBaseIpcHandler = (ipcMain) => {
const methodBase = "$aura.base";
ipcMain.on(
`${methodBase}.minimizeWindow`,
/**
*
* @param {import("electron").IpcMainEvent} _event
* @param {{ targetWindowKey: string }} arg
*/
(_event, arg) => {
const browserWindowIns = composables.getBrowserWindowInstance(
arg.targetWindowKey
);
if (!browserWindowIns) return;
browserWindowIns.minimize();
}
);
ipcMain.on(
`${methodBase}.closeWindow`,
/**
*
* @param {import("electron").IpcMainEvent} _event
* @param {{ targetWindowKey: string }} arg
*/
(_event, arg) => {
const browserWindowIns = composables.getBrowserWindowInstance(
arg.targetWindowKey
);
if (!browserWindowIns) return;
browserWindowIns.close();
}
);
ipcMain.handle(`${methodBase}.getAuraDirAsync`, (_evt, _arg) => {
return {
success: true,
data: global.__HUGO_AURA__.auraDir,
};
});
ipcMain.on(`${methodBase}.getAuraDirSync`, (event, _arg) => {
event.returnValue = {
success: true,
data: global.__HUGO_AURA__.auraDir,
};
});
};
module.exports = { applyBaseIpcHandler };

View File

@@ -0,0 +1,79 @@
// @ts-check
const __SCOPE = "main";
/**
*
* @param {import("../../../types/main/electron").AuraIPCMain} ipcMain
*/
const applyConfigIpcHandler = (ipcMain) => {
const methodBase = "$aura.config";
const mainEventBus = global.__HUGO_AURA_EVENT_BUS__;
const ConfigManager = require("../../shared/configManager");
const configManager = global.__HUGO_AURA_CONFIG_MGR__
? global.__HUGO_AURA_CONFIG_MGR__
: new ConfigManager();
ipcMain.on(`${methodBase}.refreshMainConfig`, (_event) => {
mainEventBus.emit("$aura.config.refreshConfig");
});
ipcMain.handle(
`${methodBase}.setConfigEncSettings`,
(
/** @type {import("electron").IpcMainInvokeEvent} */ _event,
/** @type {{ target: boolean }} */ arg
) => {
mainEventBus.emit("$aura.config.updateConfigEncSettings", arg.target);
return {
success: true,
};
}
);
ipcMain.on(`${methodBase}.getConfigFromMainSync`, (event, _arg) => {
if (
global.__HUGO_AURA_CONFIG__ &&
Object.keys(global.__HUGO_AURA_CONFIG__).length !== 0
) {
event.returnValue = {
success: true,
data: global.__HUGO_AURA_CONFIG__,
};
} else {
console.warn(
"[HugoAura / Main / IPC / Config / WARN] Global config var not found!"
);
event.returnValue = {
success: false,
data: {},
};
}
});
ipcMain.handle(
`${methodBase}.dispatchConfigFromRenderer`,
(_event, /** @type {{data: string, writeConfig?: boolean}} */ arg) => {
const parsedData = JSON.parse(arg.data);
global.__HUGO_AURA_CONFIG__ = parsedData;
if (arg.writeConfig) {
const result = configManager.writeConfig(parsedData);
if (!result) {
return {
success: false,
};
}
}
return {
success: true,
};
}
);
};
module.exports = { applyConfigIpcHandler };

View File

@@ -0,0 +1,13 @@
// @ts-check
const __SCOPE = "main";
/**
*
* @param {import("electron").IpcMain} ipcMain
*/
const applyDebugIpcHandler = (ipcMain) => {
const methodBase = "$aura.debug";
};
module.exports = { applyDebugIpcHandler };

View File

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

View File

@@ -0,0 +1,599 @@
// @ts-check
const __SCOPE = "main";
const { exec } = require("child_process");
const fs = require("fs");
const path = require("path");
const nodeHttps = require("https");
const { fsComposables } = require("./fsIpcHandler");
const functions = {
querySvcDetail: (
/** @type {string} */ svcName,
/** @type {string} */ findTarget
) => {
return new Promise((resolve) => {
exec(
`sc query ${svcName}`,
{ encoding: "utf8" },
(error, stdout, stderr) => {
if (error) {
resolve({
success: true,
result: false,
});
return;
}
if (stdout.includes(findTarget)) {
resolve({
success: true,
result: true,
});
} else {
resolve({
success: true,
result: false,
});
}
}
);
});
},
/**
*
* @param {string} logHeader
* @param {string} binPath
* @param {string} command
* @returns {Promise<{ success: boolean, errorObj?: Error }>}
*/
execCommand: (logHeader, binPath, command) => {
const processedPath = binPath.includes(" ") ? `"${binPath}"` : binPath;
return new Promise((resolve) => {
exec(`${processedPath} ${command}`, (error, stdout, stderr) => {
if (error) {
console.error(`${logHeader} Failed to execute command:`, error);
resolve({ success: false, errorObj: error });
return;
}
resolve({ success: true });
});
});
},
/**
*
* @param {"stable" | "alpha"} channel
* @param {(arg: DownloadTask) => any} callbackFn
* @param {string} binPath
*/
handlePLSDownload: async (channel, callbackFn, binPath) => {
// TODO: Channel selection
const apiInfo = global.__HUGO_AURA_API__;
const getVerPromise = new Promise(async (resolveGetVerReq) => {
// ↓ 目前 channel param 没有什么用处
for (const apiDomain of apiInfo.domains) {
const reqPromise = new Promise((resolveHttpRequest) => {
nodeHttps
.get(
`${apiDomain}${apiInfo.plsUpdate}?channel=${channel}`,
(rep) => {
let dataChunk = "";
rep.on("data", (chunk) => {
dataChunk += chunk;
});
rep.on("end", () => {
let parsedData = {};
try {
parsedData = JSON.parse(dataChunk);
} catch (e) {
callbackFn({
id: "",
progress: 0,
status: "struggling",
dlUrl: null,
savePath: null,
message: `数据解析失败, 正在尝试 API 域名 ${
apiInfo.domains[apiInfo.domains.indexOf(apiDomain) + 1]
} ...`,
errorObj: e,
});
setTimeout(() => {
resolveHttpRequest({
success: false,
errorObj: e,
});
}, 1000);
return;
}
resolveHttpRequest({
success: true,
data: parsedData,
});
return;
});
}
)
.on("error", (e) => {
callbackFn({
id: "",
progress: 0,
status: "struggling",
dlUrl: null,
savePath: null,
message: `连接失败, 正在尝试 API 域名 ${
apiInfo.domains[apiInfo.domains.indexOf(apiDomain) + 1]
} ...`,
errorObj: e,
});
setTimeout(() => {
resolveHttpRequest({
success: false,
errorObj: e,
});
}, 1000);
});
});
const requestResult = await reqPromise;
if (requestResult.success) {
resolveGetVerReq({
success: true,
data: requestResult.data,
});
break;
} else {
continue;
}
}
resolveGetVerReq({
success: false,
data: null,
});
});
const rawResInfo = await getVerPromise;
if (!rawResInfo.success) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: "未能获取 PLS 版本信息, 所有 API 域名均无法连接",
});
return false;
}
const plsVersionInfo = rawResInfo.data;
let deviceArch = process.env.PROCESSOR_ARCHITEW6432
? process.env.PROCESSOR_ARCHITEW6432
: process.env.PROCESSOR_ARCHITECTURE;
// @ts-expect-error
deviceArch = deviceArch.toLowerCase();
if (!Object.keys(plsVersionInfo.data.downloadUrl).includes(deviceArch)) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: `处理器架构识别失败, 检测到的架构: ${deviceArch}`,
});
return false;
}
fsComposables.downloadFile(
plsVersionInfo.data.downloadUrl[deviceArch],
binPath,
(...args) => {
if (args[0].status === "done") {
if (global.__HUGO_AURA__.plsStats) {
global.__HUGO_AURA__.plsStats.installed = true;
global.__HUGO_AURA__.plsStats.status = "dead";
}
}
callbackFn(...args);
}
);
},
};
/**
*
* @param {import("../../../types/main/electron").AuraIPCMain} ipcMain
*/
const applyPlsIpcHandler = (ipcMain) => {
const methodBase = "$aura.pls";
const PLS_INSTALL_DIR = path.join("C:\\Program Files", "HugoAura PLS", "bin");
const PLS_BIN_PATH = path.join(PLS_INSTALL_DIR, "HugoAura-PLS.exe");
const PLS_SVC_NAME = "HugoAuraPLS";
const isPlsDetached = process.argv.includes("--pls-detach");
global.__HUGO_AURA__.plsStats = {
installed: false,
launched: false,
detached: isPlsDetached,
connected: false,
version: "未知",
status: "dead",
authToken: global.__HUGO_AURA_CONFIG__.plsToken,
};
ipcMain.handle(
`${methodBase}.getPlsBinExists`,
/**
*
* @returns {{ success: boolean; data: { isExists: boolean }; error?: Error }}
*/
(_event, _arg) => {
try {
const result = fs.existsSync(PLS_BIN_PATH);
if (global.__HUGO_AURA__.plsStats?.status === "notInstalled") {
global.__HUGO_AURA__.plsStats.status = "dead";
}
return {
success: true,
data: { isExists: result },
};
} catch (e) {
// @ts-expect-error
global.__HUGO_AURA__.plsStats.status = "notInstalled";
return {
success: false,
data: { isExists: false },
error: e,
};
}
}
);
ipcMain.handle(
`${methodBase}.ensurePlsInstallDir`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {any} _arg
* @returns {{ success: boolean; data: { alreadyExists: boolean; createdDir: string; }; error?: Error }}
*/
(_event, _arg) => {
const alreadyExists = fs.existsSync(PLS_INSTALL_DIR);
if (alreadyExists) {
return {
success: true,
data: {
alreadyExists: true,
createdDir: PLS_INSTALL_DIR,
},
};
}
try {
fs.mkdirSync(PLS_INSTALL_DIR, { recursive: true });
return {
success: true,
data: {
alreadyExists: false,
createdDir: PLS_INSTALL_DIR,
},
};
} catch (error) {
return {
success: false,
data: {
alreadyExists: false,
createdDir: PLS_INSTALL_DIR,
},
error: error,
};
}
}
);
ipcMain.handle(
`${methodBase}.getPlsStats`,
/**
*
* @returns {{ success: boolean; data: import("../../../types/shared/pls/status").PLSStatus | null | undefined; }}
*/
(_event, _arg) => {
return {
success: true,
data: global.__HUGO_AURA__.plsStats,
};
}
);
ipcMain.handle(
`${methodBase}.updatePlsStats`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {import("../../../types/shared/pls/status").PLSStatus} arg
* @returns
*/
(_event, arg) => {
global.__HUGO_AURA__.plsStats = arg;
ipcMain.send("assistant", `${methodBase}.post.onPlsStatsUpdate`, arg);
return {
success: true,
};
}
);
ipcMain.handle(
`${methodBase}.getPlsSettings`,
/**
*
* @returns {{ success: boolean; data: Record<any, any> | null | undefined }}
*/
(_event, _arg) => {
return {
success: true,
data: global.__HUGO_AURA__.plsSettings,
};
}
);
ipcMain.handle(
`${methodBase}.updatePlsSettings`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {Record<any, any>} arg
* @returns
*/
(_event, arg) => {
global.__HUGO_AURA__.plsSettings = arg;
ipcMain.send("assistant", `${methodBase}.post.onPlsSettingsUpdate`, arg);
return {
success: true,
};
}
);
ipcMain.handle(
`${methodBase}.getPlsRules`,
/**
*
* @returns {{ success: boolean; data: Record<any, any> | null | undefined }}
*/
(_event, _arg) => {
return {
success: true,
data: global.__HUGO_AURA__.plsRules,
};
}
);
ipcMain.handle(
`${methodBase}.updatePlsRules`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {Record<any, any>} arg
* @returns
*/
(_event, arg) => {
global.__HUGO_AURA__.plsRules = arg;
ipcMain.send("assistant", `${methodBase}.post.onPlsRulesUpdate`, arg);
return {
success: true,
};
}
);
ipcMain.on(
`${methodBase}.ws.broadcastMessageRecv`,
/**
*
* @param {import("electron").IpcMainEvent} _event
* @param {PLSResponse} arg
*/
(_event, arg) => {
ipcMain.send("assistant", `${methodBase}.ws.post.onNewMsgRecv`, arg);
}
);
ipcMain.handle(
`${methodBase}.ws.sendWsMessage`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {ClientPLSRequest} arg
*/
(_event, arg) => {
ipcMain.send(
"auraWsKeepAlive",
`${methodBase}.ws.post.onReqSendMsg`,
arg
);
}
);
ipcMain.handle(
`${methodBase}.syncPlsConfig`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} event
* @param {{ basic: Record<any, any>, rules: Record<any, any> }} arg
*/
(event, arg) => {
global.__HUGO_AURA__.plsRules = arg.rules;
global.__HUGO_AURA__.plsSettings = arg.basic;
ipcMain.send("*", `${methodBase}.syncPlsConfig`, arg, event.sender);
return {
success: true,
};
}
);
ipcMain.handle(
`${methodBase}.plsLifecycleQuery`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {{ target: import("../../../types/shared/pls/status").PLSLifecycleType }} arg
* @returns {Promise<{ success: boolean, result: boolean }>}
*/
async (_event, arg) => {
switch (arg.target) {
case "isDetached":
return { success: true, result: isPlsDetached };
case "isSvcInstalled":
return await functions.querySvcDetail(PLS_SVC_NAME, "SERVICE_NAME");
case "isSvcStart":
return await functions.querySvcDetail(PLS_SVC_NAME, "RUNNING");
default:
console.warn(
`[HugoAura / IPC / PLS] <plsLifecycleQuery> Invalid arg.target: ${arg.target}`
);
return {
success: false,
result: false,
};
}
}
);
ipcMain.handle(
`${methodBase}.plsLifecycleControl`,
/**
*
* @param {*} _event
* @param {{ target: import("../../../types/shared/pls/status").PLSLifecycleControlType }} arg
* @returns {Promise<{ success: boolean, errorObj?: Error }>}
*/
async (_event, arg) => {
const logHeader = "[HugoAura / IPC / PLS] <plsLifecycleControl>";
switch (arg.target) {
case "instSvc":
return await functions.execCommand(
logHeader,
PLS_BIN_PATH,
"--startup auto install"
);
case "rmSvc":
return await functions.execCommand(logHeader, PLS_BIN_PATH, "remove");
case "startSvc":
return await functions.execCommand(logHeader, PLS_BIN_PATH, "start");
case "stopSvc":
return await functions.execCommand(logHeader, PLS_BIN_PATH, "stop");
case "rmBin":
const unlinkPromise = new Promise((resolve) => {
fs.unlink(PLS_BIN_PATH, (error) => {
if (error) {
resolve({
success: false,
errorObj: error,
});
console.error(
`${logHeader} Failed to remove PLS bin, error:`,
error
);
return false;
}
resolve({
success: true,
errorObj: null,
});
return true;
});
});
const unlinkRet = await unlinkPromise;
return unlinkRet;
default:
return { success: false, errorObj: new Error("Method not found") };
}
}
);
ipcMain.handle(
`${methodBase}.downloadPls`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _evt
* @param {{channel?: "stable" | "alpha", reportTo?: import("../../../types/main/core").WindowName}} arg
* @returns {void}
*/
(_evt, arg) => {
const channel = arg.channel ? arg.channel : "stable";
const reportWin = arg.reportTo ? arg.reportTo : "assistant";
functions.handlePLSDownload(
channel,
(status) => {
ipcMain.send(
reportWin,
`${methodBase}.post.reportPlsDownloadStatus`,
status
);
},
PLS_BIN_PATH
);
}
);
ipcMain.handle(
`${methodBase}.retryPlsConnect`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {any} arg
* @returns {{ success: boolean, status: "Already" | "Retrying" }}
*/
(_event, arg) => {
if (global.__HUGO_AURA__.plsStats?.connected) {
return {
success: true,
status: "Already",
};
} else {
ipcMain.send("auraWsKeepAlive", `${methodBase}.retryPlsConnect`, arg);
return {
success: true,
status: "Retrying",
};
}
}
);
ipcMain.handle(
`${methodBase}.post.updateRetryStatus`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {{ success: boolean }} arg
*/
(_event, arg) => {
ipcMain.send("assistant", `${methodBase}.post.updateRetryStatus`, arg);
return {
success: true,
};
}
);
};
module.exports = { applyPlsIpcHandler };

View File

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

View File

@@ -0,0 +1,82 @@
// @ts-check
const fs = require("fs");
const path = require("path");
class WindowHooksManager {
loadHooks() {
if (
global.__HUGO_AURA__.windowHooks &&
Object.keys(global.__HUGO_AURA__.windowHooks).length !== 0
) {
return global.__HUGO_AURA__.windowHooks;
}
const hooksPath = path.join(__dirname, "../../../aura/mainProcess/hooks");
/** @type {import("../../types/main/core").UIHooksMap} */
const hooks = new Map();
try {
const files = fs.readdirSync(hooksPath);
files.forEach((file) => {
if (!file.endsWith(".js")) return;
try {
const hook = require(path.join(hooksPath, file));
/** @type {import("../../types/main/core").WindowName} */
const targetWindow = hook.windowName || path.basename(file, ".js");
hooks.set(targetWindow, hook);
console.log(
`[HugoAura / Init / WDH] Loaded main process hook for window: ${targetWindow}`
);
} catch (err) {
console.error(
`[HugoAura / Init / WDH / Error] Failed to load main process hook ${file}:`,
err
);
}
});
} catch (err) {
console.error(
"[HugoAura / Init / WDH / Error] Failed to read hooks directory:",
err
);
}
global.__HUGO_AURA__.windowHooks = hooks;
return hooks;
}
initHookForWindow(
windowName,
centralIns,
mainProcessAppInstance,
browserWindowInstance
) {
const stripWindowName = windowName.split("_")[0];
if (!global.__HUGO_AURA__.windowHooks) return;
if (!global.__HUGO_AURA__.windowHooks.has(stripWindowName)) {
console.log(
`[HugoAura / Init / WDH] Window ${windowName} has no corresponding main process hooks, ignoring...`
);
return;
}
const { hookFunc } = global.__HUGO_AURA__.windowHooks.get(stripWindowName);
hookFunc(
centralIns,
mainProcessAppInstance,
browserWindowInstance,
windowName
);
console.log(
`[HugoAura / Init / WDH / Success / ${windowName}] Main process hook initialized.`
);
}
}
module.exports = WindowHooksManager;

20
src/aura/init/preload/webpackHook.js Normal file → Executable file
View File

@@ -1,4 +1,4 @@
const path = require('path');
const path = require("path");
class WebpackHook {
#ruleCache = new Map();
@@ -19,7 +19,7 @@ class WebpackHook {
if (!rule) {
rule = require(path.join(
__dirname,
'../../../aura/jsRewrite/',
"../../../aura/jsRewrite/",
rulePath
));
this.#ruleCache.set(rulePath, rule);
@@ -49,7 +49,7 @@ class WebpackHook {
patchModules(modules, rewrites) {
modules.forEach((mod, index) => {
if (typeof mod !== 'function') return;
if (typeof mod !== "function") return;
const stringifyFunc = mod.toString();
rewrites.forEach((rewrite) => {
@@ -67,14 +67,14 @@ class WebpackHook {
let rewrittenFunction = mod;
switch (method) {
case 'reactComponent':
case "reactComponent":
window.__HUGO_AURA_HOOK__[ruleId] = {
feature: rewrite.feature,
newFunction: rewrite.newFunction
}
newFunction: rewrite.newFunction,
};
rewrittenFunction = rewrite.preHook(mod);
break;
case 'legacy':
case "legacy":
default:
rewrittenFunction = rewrite.newFunction;
break;
@@ -95,7 +95,7 @@ class WebpackHook {
});
});
if (typeof window !== 'undefined') {
if (typeof window !== "undefined") {
window.__HUGO_AURA_DEBUG__ = {
getRuleCache: () => Array.from(this.#ruleCache.keys()),
};
@@ -105,7 +105,7 @@ class WebpackHook {
installHook(window, config) {
let realWebpackJsonp = window.webpackJsonp;
Object.defineProperty(window, 'webpackJsonp', {
Object.defineProperty(window, "webpackJsonp", {
get: () => realWebpackJsonp,
set: (value) => {
console.log(
@@ -119,7 +119,7 @@ class WebpackHook {
if (args[0] && Array.isArray(args[0][1])) {
const [chunkIds, modules] = args[0];
console.log(
`[HugoAura / AppHook] Intercepting chunk ${chunkIds.join(', ')}`
`[HugoAura / AppHook] Intercepting chunk ${chunkIds.join(", ")}`
);
const rewrites = this.loadRewriteRules(config);

181
src/aura/init/rendererHook/injection.js Normal file → Executable file
View File

@@ -24,40 +24,6 @@
});
};
const createStore = () => {
const store = {
router: "/",
};
const internal = {
get: (key) => store[key],
set: (key, value) => {
store[key] = value;
return true;
},
};
window.$store = {
get: (key) => {
const stack = new Error().stack;
if (stack.includes("aura/ui/pages/")) {
return internal.get(key);
}
return undefined;
},
set: (key, value) => {
const stack = new Error().stack;
if (stack.includes("aura/ui/pages/")) {
return internal.set(key, value);
}
return false;
},
};
return internal;
};
const store = createStore();
const createUILoader = () => {
const modules = "__TEMPLATE_TARGETS__";
const containers = new Map();
@@ -65,6 +31,63 @@
const moduleResources = new Map();
const globalScripts = new Set();
const flattenTargets = (targets, parentKey = "") => {
const flattened = {};
for (const [key, config] of Object.entries(targets)) {
const fullKey = parentKey ? `${parentKey}.${key}` : key;
flattened[fullKey] = { ...config };
if (
config.childs &&
typeof config.childs === "object" &&
!Array.isArray(config.childs)
) {
const childTargets = flattenTargets(config.childs, fullKey);
Object.assign(flattened, childTargets);
delete flattened[fullKey].childs;
}
}
return flattened;
};
const flatModules = flattenTargets(modules);
const createAccessProxy = (originalModules, flatModules) => {
const createNestedProxy = (target, path = []) => {
return new Proxy(target, {
get(target, prop) {
const currentPath = [...path, prop].join(".");
if (flatModules[currentPath]) {
return flatModules[currentPath];
}
const value = Reflect.get(target, prop);
if (typeof value === "object" && value !== null) {
return createNestedProxy(value, [...path, prop]);
}
return value;
},
set(target, prop, value) {
const currentPath = [...path, prop].join(".");
if (flatModules[currentPath]) {
flatModules[currentPath] = value;
}
return Reflect.set(target, prop, value);
},
});
};
return createNestedProxy(originalModules);
};
const accessibleModules = createAccessProxy(modules, flatModules);
const insertElement = (target, element, mode = "appendChild") => {
const elementId = element.id;
if (document.getElementById(elementId)) {
@@ -120,12 +143,12 @@
const observer = new MutationObserver((_mutations) => {
if (!document.getElementById(elementId)) {
let targetElement = document.querySelector(
modules[moduleKey].pageSelector
flatModules[moduleKey].pageSelector
);
if (
targetElement &&
modules[moduleKey].active &&
modules[moduleKey].revive
flatModules[moduleKey].active &&
flatModules[moduleKey].revive
) {
if (!document.getElementById(elementId)) {
console.log(
@@ -145,12 +168,53 @@
observers.set(moduleKey, observer);
};
const loadResources = async (resources, type, moduleKey, isRevive) => {
if (!resources || isRevive) return [];
const resourceArray = Array.isArray(resources) ? resources : [resources];
const loadedResources = [];
for (const resource of resourceArray) {
try {
let element;
if (type === "css") {
element = document.createElement("link");
element.rel = "stylesheet";
element.href = `../../aura/${resource}`;
document.head.appendChild(element);
} else if (type === "js") {
element = document.createElement("script");
element.src = `../../aura/${resource}`;
document.body.appendChild(element);
await new Promise((resolve, reject) => {
element.onload = resolve;
element.onerror = reject;
});
}
if (element) {
loadedResources.push(element);
console.log(
`[HugoAura / UI / ${moduleKey}] Loaded ${type}: ${resource}`
);
}
} catch (err) {
console.error(
`[HugoAura / UI / Error] Failed to load ${type} ${resource} for ${moduleKey}:`,
err
);
}
}
return loadedResources;
};
const loader = {
async loadModule(moduleKey, isRevive = false) {
if (!modules[moduleKey]?.active) return;
if (!flatModules[moduleKey]?.active) return;
try {
const config = modules[moduleKey];
const config = flatModules[moduleKey];
const target = await waitForElement(config.pageSelector);
const elementId = `aura-container-${moduleKey.replace(/\./g, "-")}`;
@@ -168,12 +232,14 @@
const resources = new Set();
moduleResources.set(moduleKey, resources);
if (config.pageCSS && !isRevive) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = `../../aura/${config.pageCSS}`;
document.head.appendChild(link);
resources.add(link);
if (config.pageCSS) {
const cssResources = await loadResources(
config.pageCSS,
"css",
moduleKey,
isRevive
);
cssResources.forEach((resource) => resources.add(resource));
}
const html = await fetch(`../../aura/${config.pageURI}`).then((r) =>
@@ -184,18 +250,21 @@
insertElement(target, container, config.selectorMode);
monitorParent(moduleKey, target, container, config.selectorMode);
if (config.pageScript && !isRevive) {
const script = document.createElement("script");
script.src = `../../aura/${config.pageScript}`;
document.body.appendChild(script);
resources.add(script);
if (config.pageScript) {
const jsResources = await loadResources(
config.pageScript,
"js",
moduleKey,
isRevive
);
jsResources.forEach((resource) => resources.add(resource));
}
const observer = new MutationObserver(() => {
if (
!document.contains(container) &&
modules[moduleKey].active &&
modules[moduleKey].revive
flatModules[moduleKey].active &&
flatModules[moduleKey].revive
) {
this.loadModule(moduleKey, true);
}
@@ -238,13 +307,13 @@
handleModuleChange(moduleKey, path = []) {
const fullPath = [...path, moduleKey].join(".");
if (path.length === 0 && modules[moduleKey].active) {
if (path.length === 0 && flatModules[moduleKey].active) {
this.loadModule(moduleKey);
} else if (path.length === 0) {
this.unloadModule(moduleKey);
} else {
if (moduleKey === "active") {
if (modules[path[0]].active) {
if (flatModules[path[0]].active) {
this.loadModule(path[0]);
} else {
this.unloadModule(path[0]);
@@ -297,7 +366,7 @@
try {
await loadGlobalJS();
for (const [key, config] of Object.entries(modules)) {
for (const [key, config] of Object.entries(flatModules)) {
if (config.active) {
await loader.loadModule(key);
}
@@ -309,7 +378,7 @@
initialLoad();
return createDeepProxy(modules, (path, prop, value) => {
return createDeepProxy(accessibleModules, (path, prop, value) => {
loader.handleModuleChange(prop, path);
});
};

0
src/aura/init/rendererHook/networkHook.js Normal file → Executable file
View File

View File

@@ -1,13 +1,20 @@
// @ts-check
const fs = require("fs");
const path = require("path");
class HooksManager {
class RendererHooksManager {
loadHooks() {
if (global.__HUGO_AURA__.hooks) {
return global.__HUGO_AURA__.hooks;
if (
global.__HUGO_AURA__.uiHooks &&
Object.keys(global.__HUGO_AURA__.uiHooks).length !== 0
) {
return global.__HUGO_AURA__.uiHooks;
}
const hooksPath = path.join(__dirname, "../../../aura/ui/hooks");
const hooksPath = path.join(__dirname, "../../../aura/ui/hookDefinitions");
/** @type {import("../../types/main/core").UIHooksMap} */
const hooks = new Map();
try {
@@ -17,63 +24,82 @@ class HooksManager {
try {
const hook = require(path.join(hooksPath, file));
/** @type {import("../../types/main/core").WindowName} */
const targetWindow = hook.windowName || path.basename(file, ".js");
hooks.set(targetWindow, hook);
console.log(
`[HugoAura / Init] Loaded hook for window: ${targetWindow}`
`[HugoAura / Init / RDH] Loaded ui hook for window: ${targetWindow}`
);
} catch (err) {
console.error(
`[HugoAura / Init / Error] Failed to load hook ${file}:`,
`[HugoAura / Init / RDH / Error] Failed to load ui hook ${file}:`,
err
);
}
});
} catch (err) {
console.error(
"[HugoAura / Init / Error] Failed to read hooks directory:",
"[HugoAura / Init / RDH / Error] Failed to ui hooks directory:",
err
);
}
global.__HUGO_AURA__.hooks = hooks;
global.__HUGO_AURA__.uiHooks = hooks;
return hooks;
}
cleanupWindow(windowKey, listeners) {
/**
*
* @param {import("../../types/main/core").WindowName} windowKey
* @param {import("../../types/main/core").HookedWindow} hookedWindowProps
*/
cleanupWindow(windowKey, hookedWindowProps) {
console.log(
`[HugoAura / Cleanup / ${windowKey}] Window destroyed, cleaning up...`
`[HugoAura / Cleanup / RDH / ${windowKey}] Window destroyed, cleaning up...`
);
if (listeners) {
const { webContents, domReadyListener, destroyedListener } = listeners;
if (hookedWindowProps) {
const { webContents, domReadyListener, destroyedListener } =
hookedWindowProps;
webContents.removeListener("dom-ready", domReadyListener);
webContents.removeListener("destroyed", destroyedListener);
}
// @ts-expect-error
global.__HUGO_AURA__.hookedWindows.delete(windowKey);
}
/**
*
* @param {import("electron").WebContents} webContents
* @param {import("../../types/render/uiHook").UIHookConfigFin} hookConfig
* @param {import("../../types/main/core").WindowName} windowName
* @returns
*/
handleWindowHook(webContents, hookConfig, windowName) {
if (!hookConfig) return;
/** @type {import("../../types/main/core").WindowName} */
const windowKey = `${hookConfig.windowName || windowName}`;
// @ts-expect-error
if (global.__HUGO_AURA__.hookedWindows.has(windowKey)) {
console.log(
`[HugoAura / Init] Duplicate hook for ${windowKey}, ignoring...`
`[HugoAura / Init / RDH] Duplicate ui hook for ${windowKey}, ignoring...`
);
return;
}
console.log(`[HugoAura / Init] Hook is initializing for ${windowKey}...`);
console.log(
`[HugoAura / Init] Hook loaded at: ${new Date().toISOString()}`
`[HugoAura / Init / RDH] UI Hook is initializing for ${windowKey}...`
);
console.log(
`[HugoAura / Init / RDH] UI Hook loaded at: ${new Date().toISOString()}`
);
const domReadyListener = () => {
try {
console.log(
`[HugoAura / UI / Verb / ${windowKey}] Loading injection script...`
`[HugoAura / RDH / Verb / ${windowKey}] Loading injection script...`
);
const injectionScript = fs
@@ -93,18 +119,18 @@ class HooksManager {
.executeJavaScript(injectionScript, true)
.then(() =>
console.log(
`[HugoAura / UI / Done / ${windowKey}] Injection script executed`
`[HugoAura / RDH / Done / ${windowKey}] Injection script executed`
)
)
.catch((err) =>
console.error(
`[HugoAura / UI / Error / ${windowKey}] Failed to execute injection script:`,
`[HugoAura / RDH / Error / ${windowKey}] Failed to execute injection script:`,
err
)
);
} catch (err) {
console.error(
`[HugoAura / UI / Error / ${windowKey}] Failed to load UI hook:`,
`[HugoAura / RDH / Error / ${windowKey}] Failed to load UI hook:`,
err
);
}
@@ -113,6 +139,7 @@ class HooksManager {
const destroyedListener = () => {
this.cleanupWindow(
windowKey,
// @ts-expect-error
global.__HUGO_AURA__.hookedWindows.get(windowKey)
);
};
@@ -120,6 +147,7 @@ class HooksManager {
webContents.on("dom-ready", domReadyListener);
webContents.on("destroyed", destroyedListener);
// @ts-expect-error
global.__HUGO_AURA__.hookedWindows.set(windowKey, {
webContents,
domReadyListener,
@@ -127,9 +155,9 @@ class HooksManager {
});
console.log(
`[HugoAura / Init / Success / ${windowKey}] Hook initialized successfully!`
`[HugoAura / Init / RDH / Success / ${windowKey}] UI Hook initialized.`
);
}
}
module.exports = HooksManager;
module.exports = RendererHooksManager;

733
src/aura/init/shared/configManager.js Normal file → Executable file
View File

@@ -1,11 +1,38 @@
const fs = require('fs');
const path = require('path');
const os = require('os');
// @ts-check
const fs = require("fs");
const path = require("path");
const os = require("os");
const crypto = require("crypto");
const childProc = require("child_process");
const RegistryManagerClass = require("./registryManager");
const registryManager = new RegistryManagerClass();
// Constants
const CRYPTO_SETTINGS_AES = {
mode: "aes-256-gcm",
keyLength: 32,
keyIter: 100000,
ivLength: 12,
tagLength: 16,
saltLength: 16,
obfuscateStr: "eCybsseK",
hash: "sha256",
};
const LMAK_SETTINGS_BASE = "EncSettings\\LMAK";
/**
*
* @param {Record<any, any>} target
* @param {Record<any, any>} source
* @returns
*/
const deepMerge = (target, source) => {
const result = JSON.parse(JSON.stringify(target));
if (!source || typeof source !== 'object') {
if (!source || typeof source !== "object") {
return {};
}
@@ -15,9 +42,9 @@ const deepMerge = (target, source) => {
if (!(key in source)) {
keysToDelete.push(key);
} else if (
typeof result[key] === 'object' &&
typeof result[key] === "object" &&
result[key] !== null &&
typeof source[key] === 'object' &&
typeof source[key] === "object" &&
source[key] !== null
) {
result[key] = deepMerge(result[key], source[key]);
@@ -42,29 +69,80 @@ const deepMerge = (target, source) => {
class ConfigManager {
constructor() {
this.configPath = path.join(
this.configDir = global.__HUGO_AURA__.auraDir;
this.configPath = path.join(this.configDir, "config.json");
this.encConfigPath = path.join(this.configDir, ".cache_2eafc8d0.dat"); // (雾
/* ↑ 不使用 .tmp 扩展名, 不然容易真被清理了 */
this.defaultConfigPath = path.join(__dirname, "default.json");
this.useEncConfig = false;
this.isConfigReadFailed = false;
this.side = "unknown";
if (fs.existsSync(this.encConfigPath)) {
this.useEncConfig = true;
} else {
this.useEncConfig = false;
}
if (global.__HUGO_AURA_EVENT_BUS__) {
// Expect always true
global.__HUGO_AURA_EVENT_BUS__.on(
"$aura.config.updateConfigEncSettings",
(/** @type {boolean} */ newVal) => {
this.useEncConfig = newVal;
}
);
}
}
migrateOldConfigFile() {
if (this.configDir === path.join(os.homedir(), "Documents", "HugoAura")) {
return;
}
const oldConfigPath = path.join(
os.homedir(),
'Documents',
'HugoAura',
'config.json'
"Documents",
"HugoAura",
"config.json"
);
const oldEncConfigPath = path.join(
os.homedir(),
"Documents",
"HugoAura",
".cache_2eafc8d0.dat"
);
if (fs.existsSync(oldConfigPath)) {
fs.copyFileSync(oldConfigPath, this.configPath);
fs.unlinkSync(oldConfigPath);
} else if (fs.existsSync(oldEncConfigPath)) {
fs.copyFileSync(oldEncConfigPath, this.encConfigPath);
fs.unlinkSync(oldEncConfigPath);
this.useEncConfig = true;
}
console.log(
`[HugoAura / Config] Moved old config file to ${this.configDir}`
);
this.defaultConfigPath = path.join(__dirname, 'default.json');
}
getHugoAuraConfigPath() {
return path.dirname(this.configPath);
return path.dirname(
this.useEncConfig ? this.encConfigPath : this.configPath
);
}
getConfigPath() {
return this.configPath;
return this.useEncConfig ? this.encConfigPath : this.configPath;
}
getDefaultConfig() {
try {
return JSON.parse(fs.readFileSync(this.defaultConfigPath, 'utf8'));
return JSON.parse(fs.readFileSync(this.defaultConfigPath, "utf8"));
} catch (err) {
console.warn(
'[HugoAura / Config] No default config found, using empty config'
"[HugoAura / Config] No default config found, using empty config"
);
return { rewrite: {} };
}
@@ -74,12 +152,12 @@ class ConfigManager {
if (global.__HUGO_AURA__.configInit) return;
const hugoAuraPath = this.getHugoAuraConfigPath();
if (!fs.existsSync(hugoAuraPath)) {
console.log('[HugoAura / Config] Creating HugoAura directory');
console.log("[HugoAura / Config] Creating HugoAura directory");
fs.mkdirSync(hugoAuraPath, { recursive: true });
}
if (!fs.existsSync(this.configPath)) {
console.log('[HugoAura / Config] Creating default config file');
if (!fs.existsSync(this.configPath) && !fs.existsSync(this.encConfigPath)) {
console.log("[HugoAura / Config] Creating default config file");
const defaultConfig = this.getDefaultConfig();
this.writeConfig(defaultConfig);
}
@@ -87,25 +165,66 @@ class ConfigManager {
readConfig() {
try {
const config = JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
console.log('[HugoAura / Config] Successfully loaded config:', config);
let config = {};
if (this.useEncConfig) {
const hashedPasswdResultObj = this.retrieveEncPassword();
if (hashedPasswdResultObj.success && hashedPasswdResultObj.data) {
config = this.decryptConfig(hashedPasswdResultObj.data).data;
if (!config) {
this.isConfigReadFailed = true;
return this.getDefaultConfig(); // should be changed, too
}
} else {
console.error("[HugoAura / Config / ERROR] Failed to decrypt config");
this.isConfigReadFailed = true;
return this.getDefaultConfig(); // This behaviour should be changed later
}
} else {
config = JSON.parse(fs.readFileSync(this.configPath, "utf8"));
}
// console.log("[HugoAura / Config] Successfully loaded config:", config);
if (this.isConfigReadFailed) this.isConfigReadFailed = false;
return config;
} catch (err) {
console.error('[HugoAura / Config] Failed to read config:', err);
console.error("[HugoAura / Config] Failed to read config:", err);
this.isConfigReadFailed = true;
return this.getDefaultConfig();
}
}
writeConfig(config) {
/**
*
* @param {Record<any, any>} config
* @returns {Promise<boolean>}
*/
async writeConfig(config) {
try {
fs.writeFileSync(
this.configPath,
JSON.stringify(config, null, 2),
'utf8'
);
if (this.useEncConfig) {
const hashedPasswdResultObj = await this.retrieveEncPasswordAsync();
if (hashedPasswdResultObj.success && hashedPasswdResultObj.data) {
this.encryptConfig(config, hashedPasswdResultObj.data);
} else {
console.error(
"[HugoAura / Config / Write / ERROR] Failed to write config: Retrieve enc password failed"
);
return false;
}
} else {
fs.writeFileSync(
this.configPath,
JSON.stringify(config, null, 2),
"utf8"
);
}
if (this.side === "renderer") {
global.ipcRenderer.send("$aura.config.refreshMainConfig");
}
return true;
} catch (err) {
console.error('[HugoAura / Config] Failed to write config:', err);
console.error("[HugoAura / Config] Failed to write config:", err);
return false;
}
}
@@ -114,24 +233,572 @@ class ConfigManager {
let defaultConfig = this.getDefaultConfig();
let config = {};
try {
if (fs.existsSync(this.configPath)) {
const userConfig = JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
if (fs.existsSync(this.configPath) || fs.existsSync(this.encConfigPath)) {
const userConfig = this.readConfig();
if (global.__HUGO_AURA__.configInit) {
config = userConfig;
return userConfig;
} else {
config = deepMerge(userConfig, defaultConfig);
console.log('[HugoAura / Config] Merged with user config');
console.log("[HugoAura / Config] Merged with user config");
this.writeConfig(config);
}
}
} catch (err) {
console.error('[HugoAura / Config] Failed to load user config:', err);
console.error("[HugoAura / Config] Failed to load user config:", err);
this.isConfigReadFailed = true;
config = defaultConfig;
}
return config;
}
priv_getMacAddr() {
const netInf = os.networkInterfaces();
const realInfs = Object.keys(netInf).filter(
(key) =>
!key.includes("Pseudo") &&
!key.includes("Loopback") &&
!key.includes("Virtual") &&
!key.includes("Tunnel") &&
!key.includes("Cisco") &&
!key.includes("VPN")
);
/**
*
* @param {string[]} infNames
* @param {Record<string, any>} infObj
* @returns
*/
const getValidInfMac = (infNames, infObj) => {
for (const name of infNames) {
const target = infObj[name][0];
const isValid = !target.internal && target.mac !== "00:00:00:00:00:00";
if (isValid) {
return target.mac;
}
}
return null;
};
const rawInfMac = getValidInfMac(realInfs, netInf);
const macAddr = rawInfMac
? rawInfMac.replace(/:/g, "").toUpperCase()
: null;
return macAddr;
}
/**
*
* @param {SHA256EncryptedPassword} password
*/
async saveEncPassword(password) {
let macAddr = this.priv_getMacAddr();
let fallbackToStaticKey = false;
if (!macAddr) {
console.warn(
"[HugoAura / Config / LMK] No valid network inf found, fallback to static key."
);
macAddr = Buffer.from(crypto.randomBytes(6))
.toString("hex")
.toUpperCase();
}
const randomSalt = crypto.randomBytes(CRYPTO_SETTINGS_AES.saltLength);
const key = crypto.scryptSync(macAddr, randomSalt, 32);
const iv = crypto.randomBytes(CRYPTO_SETTINGS_AES.ivLength);
const cipherIns = crypto.createCipheriv(CRYPTO_SETTINGS_AES.mode, key, iv, {
// @ts-expect-error
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
});
let encryptedPassword = cipherIns.update(password, "utf-8", "hex");
encryptedPassword += cipherIns.final("hex");
const authTagHex = cipherIns.getAuthTag().toString("hex");
const ivHex = iv.toString("hex");
const saltHex = randomSalt.toString("hex");
await registryManager.createOrUpdateRegKey(
LMAK_SETTINGS_BASE,
"LMAK_Value",
encryptedPassword,
true
);
await registryManager.createOrUpdateRegKey(
LMAK_SETTINGS_BASE,
"LMAK_IV",
ivHex,
true
);
await registryManager.createOrUpdateRegKey(
LMAK_SETTINGS_BASE,
"LMAK_Salt",
saltHex,
true
);
await registryManager.createOrUpdateRegKey(
LMAK_SETTINGS_BASE,
"LMAK_AuthTag",
authTagHex,
true
);
if (fallbackToStaticKey) {
await registryManager.createOrUpdateRegKey(
LMAK_SETTINGS_BASE,
"LMAK_FakeMac",
macAddr,
true
);
}
return true;
}
retrieveEncPassword() {
try {
const authTagHex = registryManager.readRegKeySync(
LMAK_SETTINGS_BASE,
"LMAK_AuthTag",
true
)?.data;
const ivHex = registryManager.readRegKeySync(
LMAK_SETTINGS_BASE,
"LMAK_IV",
true
)?.data;
const saltHex = registryManager.readRegKeySync(
LMAK_SETTINGS_BASE,
"LMAK_Salt",
true
)?.data;
const encPasswdHex = registryManager.readRegKeySync(
LMAK_SETTINGS_BASE,
"LMAK_Value",
true
)?.data;
let isStaticKey = false;
let macAddr = null;
try {
macAddr = registryManager.readRegKeySync(
LMAK_SETTINGS_BASE,
"LMAK_FakeMac",
true
)?.data;
if (!macAddr) {
isStaticKey = false;
} else {
isStaticKey = true;
}
} catch {
isStaticKey = false;
}
if (!isStaticKey) {
macAddr = this.priv_getMacAddr();
if (!macAddr) {
console.error(
"[HugoAura / Config / ERROR] Failed to retrieve password from reg: MAC Address invalid."
);
return {
success: false,
data: null,
error: new Error("Mac is null or undefined"),
};
}
}
if (!saltHex || !ivHex || !authTagHex || !encPasswdHex) {
console.error(
"[HugoAura / Config / ERROR] Failed to retrieve password from reg: Reg keys invalid."
);
return {
success: false,
data: null,
error: new Error("Reg key invalid"),
};
}
const salt = Buffer.from(saltHex, "hex");
const iv = Buffer.from(ivHex, "hex");
const authTag = Buffer.from(authTagHex, "hex");
const encPasswd = Buffer.from(encPasswdHex, "utf-8").toString();
const key = crypto.scryptSync(macAddr, salt, 32);
const decipherIns = crypto.createDecipheriv(
CRYPTO_SETTINGS_AES.mode,
key,
iv,
{
// @ts-expect-error
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
}
);
decipherIns.setAuthTag(authTag);
const result = Buffer.concat([
decipherIns.update(encPasswd, "hex"),
decipherIns.final(),
]).toString();
return {
success: true,
data: result,
error: null,
};
} catch (e) {
console.error(
"[HugoAura / Config / ERROR] Unexpected error occurred while retrieving password from reg, error:",
e
);
return {
success: false,
data: null,
error: e,
};
}
}
async retrieveEncPasswordAsync() {
try {
const authTagHex = (
await registryManager.readRegKey(
LMAK_SETTINGS_BASE,
"LMAK_AuthTag",
true
)
).data;
const ivHex = (
await registryManager.readRegKey(LMAK_SETTINGS_BASE, "LMAK_IV", true)
).data;
const saltHex = (
await registryManager.readRegKey(LMAK_SETTINGS_BASE, "LMAK_Salt", true)
).data;
const encPasswdHex = (
await registryManager.readRegKey(LMAK_SETTINGS_BASE, "LMAK_Value", true)
).data;
let isStaticKey = false;
let macAddr = null;
try {
macAddr = (
await registryManager.readRegKey(
LMAK_SETTINGS_BASE,
"LMAK_FakeMac",
true
)
).data;
if (!macAddr) {
isStaticKey = false;
} else {
isStaticKey = true;
}
} catch {
isStaticKey = false;
}
if (!isStaticKey) {
macAddr = this.priv_getMacAddr();
if (!macAddr) {
console.error(
"[HugoAura / Config / ERROR] Failed to retrieve password from reg: MAC Address invalid."
);
return {
success: false,
data: null,
error: new Error("Mac is null or undefined"),
};
}
}
if (!saltHex || !ivHex || !authTagHex || !encPasswdHex) {
console.error(
"[HugoAura / Config / ERROR] Failed to retrieve password from reg: Reg keys invalid."
);
return {
success: false,
data: null,
error: new Error("Reg key invalid"),
};
}
const salt = Buffer.from(saltHex, "hex");
const iv = Buffer.from(ivHex, "hex");
const authTag = Buffer.from(authTagHex, "hex");
const encPasswd = Buffer.from(encPasswdHex, "utf-8").toString();
const key = crypto.scryptSync(macAddr, salt, 32);
const decipherIns = crypto.createDecipheriv(
CRYPTO_SETTINGS_AES.mode,
key,
iv,
{
// @ts-expect-error
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
}
);
decipherIns.setAuthTag(authTag);
const result = Buffer.concat([
decipherIns.update(encPasswd, "hex"),
decipherIns.final(),
]).toString();
return {
success: true,
data: result,
error: null,
};
} catch (e) {
console.error(
"[HugoAura / Config / ERROR] Unexpected error occurred while retrieving password from reg, error:",
e
);
return {
success: false,
data: null,
error: e,
};
}
}
async clearEncPasswdRegKey() {
await registryManager.delRegKey(LMAK_SETTINGS_BASE, null);
}
/**
*
* @param {Record<any, any>} configData
* @param {SHA256EncryptedPassword} passwd
*/
encryptConfig(configData, passwd) {
registryManager.initRegistrySync();
const salt = crypto.randomBytes(CRYPTO_SETTINGS_AES.saltLength);
const key = crypto.pbkdf2Sync(
passwd,
salt,
CRYPTO_SETTINGS_AES.keyIter,
CRYPTO_SETTINGS_AES.keyLength,
CRYPTO_SETTINGS_AES.hash
);
const iv = crypto.randomBytes(CRYPTO_SETTINGS_AES.ivLength);
const cipherIns = crypto.createCipheriv(CRYPTO_SETTINGS_AES.mode, key, iv, {
// @ts-expect-error
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
});
const stringifyConfigData = JSON.stringify(configData);
let encryptedCfg = cipherIns.update(stringifyConfigData, "utf-8", "hex");
encryptedCfg += cipherIns.final("hex");
const authTag = cipherIns.getAuthTag();
/** @type {EncryptedConfig} */
const encConfigFinal = {};
encConfigFinal.content = encryptedCfg;
encConfigFinal.authTag = authTag.toString("base64");
encConfigFinal.salt = salt.toString("base64");
encConfigFinal.iv = iv.toString("base64");
const base64EncConfig =
CRYPTO_SETTINGS_AES.obfuscateStr +
Buffer.from(JSON.stringify(encConfigFinal)).toString("base64");
try {
fs.writeFileSync(this.encConfigPath, base64EncConfig, "utf-8");
try {
fs.unlinkSync(this.configPath);
} catch {
console.debug("[HugoAura / Config] Dec config not exists, skipping...");
}
if (!this.useEncConfig) this.useEncConfig = true;
return true;
} catch (err) {
console.error(
"[HugoAura / Config] Failed to write encrypted config:",
err
);
console.error(
"[HugoAura / Config] Pending config data:",
base64EncConfig
);
return false;
}
}
/**
*
* @param {SHA256EncryptedPassword} passwd
* @returns {{success: boolean, data: AuraConfig}}
*/
decryptConfig(passwd) {
try {
const FAILED_RET = {
success: false,
data: {},
};
let base64EncConfig = null;
try {
if (!fs.existsSync(this.encConfigPath)) {
return FAILED_RET;
}
base64EncConfig = fs.readFileSync(this.encConfigPath, "utf-8");
} catch (err) {
console.error(
"[HugoAura / Config] Failed to read encrypted config:",
err
);
return FAILED_RET;
}
if (base64EncConfig) {
const strip64EncCfg = base64EncConfig.split(
CRYPTO_SETTINGS_AES.obfuscateStr
)[1];
const encryptCfg = Buffer.from(strip64EncCfg, "base64").toString(
"utf-8"
);
/** @type {null | EncryptedConfig} */
let parsedEncCfg = null;
try {
parsedEncCfg = JSON.parse(encryptCfg);
} catch (err) {
console.error(
"[HugoAura / Config] Failed to parse encrypted config:",
err
);
console.error("[HugoAura / Config] Pending data:", encryptCfg);
}
if (parsedEncCfg === null) return FAILED_RET;
const salt = Buffer.from(parsedEncCfg.salt, "base64");
const iv = Buffer.from(parsedEncCfg.iv, "base64");
const authTag = Buffer.from(parsedEncCfg.authTag, "base64");
const key = crypto.pbkdf2Sync(
passwd,
salt,
CRYPTO_SETTINGS_AES.keyIter,
CRYPTO_SETTINGS_AES.keyLength,
CRYPTO_SETTINGS_AES.hash
);
const decipherIns = crypto.createDecipheriv(
CRYPTO_SETTINGS_AES.mode,
key,
iv,
{
// @ts-expect-error
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
}
);
decipherIns.setAuthTag(authTag);
const stringifyDecCfg = Buffer.concat([
decipherIns.update(parsedEncCfg.content, "hex"),
decipherIns.final(),
]).toString();
/** @type {null | Record<any, any>} */
let decConfig = null;
try {
decConfig = JSON.parse(stringifyDecCfg);
} catch (err) {
console.error(
"[HugoAura / Config] Failed to parse decrypted config:",
err
);
console.error("[HugoAura / Config] Pending data:", decConfig);
return FAILED_RET;
}
if (decConfig === null) return FAILED_RET;
// console.debug(decConfig);
return {
success: true,
data: decConfig,
};
} else {
console.error(
"[HugoAura / Config] Unexpected error occurred while decrypting config: base64EncConfig is undefined"
);
return FAILED_RET;
}
} catch (e) {
console.error(
"[HugoAura / Config] Unexpected error occurred while decrypting config:",
e
);
return {
success: false,
data: {},
};
}
}
/**
*
* @param {Record<any, any> | null} curConfig
* @param {SHA256EncryptedPassword | undefined | null} passwd
* @returns {Promise<{success: boolean}>}
*/
async switchToDecConfig(curConfig, passwd = null) {
let decConfig = null;
if (!curConfig && passwd) {
const getDecConfigResult = this.decryptConfig(passwd);
if (
!getDecConfigResult?.success ||
!getDecConfigResult.data ||
Object.keys(getDecConfigResult.data).length === 0
) {
console.error(
"[HugoAura / Config] Failed to switch to decrypted config: Error decrypting config"
);
return {
success: false,
};
}
decConfig = getDecConfigResult.data;
}
this.useEncConfig = false;
await this.clearEncPasswdRegKey();
// @ts-expect-error
this.writeConfig(curConfig ? curConfig : decConfig);
try {
fs.unlinkSync(this.encConfigPath);
} catch {
console.debug("[HugoAura / Config] Enc config not exists, skipping...");
}
global.__HUGO_AURA_EVENT_BUS__.emit(
"$aura.config.updateConfigEncSettings",
false
);
if (this.side === "renderer") {
global.ipcRenderer.invoke("$aura.config.setConfigEncSettings", {
target: false,
});
global.ipcRenderer.invoke("$aura.config.dispatchConfigFromRenderer", {
data: JSON.stringify(curConfig),
});
}
return {
success: true,
};
}
}
module.exports = new ConfigManager();
module.exports = ConfigManager;

33
src/aura/init/shared/default.json Normal file → Executable file
View File

@@ -8,6 +8,14 @@
"salt": "aura"
},
"authModeRewrite": "default"
},
"vendor/screenLock": {
"enabled": true,
"disableKeyboardHook": false,
"authRewriteType": "customActivationCode",
"customActivationCode": {
"activationCodeWithSalt": "cbbd87c419b1c2dbc412ae238f1f4be3"
}
}
},
"networkRewrite": {
@@ -18,5 +26,30 @@
"enabled": true
}
},
"ssa": {
"ux": {
"easiAssistant": {
"autoHide": false,
"notDisplay": false
}
}
},
"auraSettings": {
"settingsPasswordEnabled": false,
"settingsPasswordWithSalt": "32703D292460CC9A3B867494D6AD9A8E4A3ADF0FAA4D6867BC4D412CC3927D02E47C6D0B1763BB53E57B2241C6193433561CDA09D7C48CA03983072B876F0965",
"encryptConfig": false,
"appearance": {
"appBar": {
"actionBtnsOnRight": false
}
},
"uiAccessMethod": {
"showEntryIcon": true,
"fallbackAccessMethods": {
"hotkey": false,
"touch": false
}
}
},
"devTools": false
}

View File

@@ -0,0 +1,489 @@
// @ts-check
const { exec, execSync } = require("child_process");
// Constants
const LOG_PREFIX = "[HugoAura / Init / Reg";
const LOG_PREFIX_FUNC = "[HugoAura / Reg";
const AURA_REGISTRY_PATH = [
"HKEY_USERS",
".DEFAULT",
"SOFTWARE",
"HugoAura",
].join("\\");
class RegistryManager {
/**
* @param {string} [path]
* @returns {Promise<boolean>}
*/
async handleCreateReg(path) {
try {
const { stdout } = await new Promise((resolve, reject) => {
exec(
["reg", "add", path].join(" "),
{ encoding: "utf8" },
(error, stdout, stderr) => {
if (error) reject(error);
else resolve({ stdout, stderr });
}
);
});
console.log(
`${LOG_PREFIX} / SUCCESS] Registry path ${path} successfully created.`
);
console.debug(`${LOG_PREFIX} / DEBUG] Reg add command stdout:`, stdout);
return true;
} catch (e) {
console.error(
`${LOG_PREFIX} / ERROR] Failed creating registry path, error:`,
e
);
return false;
}
}
/**
* @param {string} [path]
* @returns {boolean}
*/
handleCreateRegSync(path) {
try {
const createResult = execSync(["reg", "add", path].join(" "), {
encoding: "utf8",
});
if (createResult) {
console.log(
`${LOG_PREFIX} / SUCCESS] Registry path ${path} successfully created.`
);
console.debug(
`${LOG_PREFIX} / DEBUG] Reg add command stdout:`,
createResult
);
return true;
} else {
return false;
}
} catch (e) {
console.error(
`${LOG_PREFIX} / ERROR] Failed creating registry path, error:`,
e
);
return false;
}
}
/**
* @returns {Promise<boolean>}
*/
async initRegistry() {
try {
const { stdout } = await new Promise((resolve, reject) => {
exec(
["reg", "query", AURA_REGISTRY_PATH].join(" "),
{ encoding: "utf8" },
(error, stdout, stderr) => {
if (error) reject(error);
else resolve({ stdout, stderr });
}
);
});
console.log(`${LOG_PREFIX}] Registry check up success.`);
console.debug(`${LOG_PREFIX}] Command stdout:`, stdout);
return true;
} catch (e) {
console.warn(`${LOG_PREFIX} / WARN] Failed to query registry, error:`, e);
return await this.handleCreateReg(AURA_REGISTRY_PATH);
}
}
/**
* @returns {boolean}
*/
initRegistrySync() {
try {
const queryResult = execSync(
["reg", "query", AURA_REGISTRY_PATH].join(" "),
{ encoding: "utf8" }
);
if (queryResult) {
console.log(`${LOG_PREFIX}] Registry check up success.`);
console.debug(`${LOG_PREFIX}] Command stdout:`, queryResult);
return true;
} else {
return false;
}
} catch (e) {
console.warn(`${LOG_PREFIX} / WARN] Failed to query registry, error:`, e);
return this.handleCreateRegSync(AURA_REGISTRY_PATH);
}
}
/**
* @param {string} relativePath
* @param {string} keyName
* @param {string} keyVal
* @param {boolean | undefined} silent
* @returns {Promise<{ success: boolean, error: Error | null }>}
*/
async createOrUpdateRegKey(relativePath, keyName, keyVal, silent = false) {
try {
const { stdout } = await new Promise((resolve, reject) => {
exec(
[
"reg",
"add",
[AURA_REGISTRY_PATH, relativePath].join("\\"),
"/v",
keyName,
"/t",
"REG_SZ",
"/d",
`\"${keyVal}\"`,
"/f",
].join(" "),
{ encoding: "utf8" },
(error, stdout, stderr) => {
if (error) reject(error);
else resolve({ stdout, stderr });
}
);
});
if (!silent) {
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully created / updated reg key ${relativePath}/${keyName} with data: ${keyVal}`
);
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Add key command stdout:`,
stdout
);
}
return {
success: true,
error: null,
};
} catch (e) {
console.error(
`${LOG_PREFIX_FUNC} / ERROR] Failed to create / update reg key, error:`,
silent ? "<Hidden>" : e
);
return {
success: false,
error: e,
};
}
}
/**
* @param {string} relativePath
* @param {string} keyName
* @param {string} keyVal
* @param {boolean | undefined} silent
* @returns {{ success: boolean, error: Error | null }}
*/
createOrUpdateRegKeySync(relativePath, keyName, keyVal, silent = false) {
try {
const result = execSync(
[
"reg",
"add",
[AURA_REGISTRY_PATH, relativePath].join("\\"),
"/v",
keyName,
"/t",
"REG_SZ",
"/d",
`\"${keyVal}\"`,
"/f",
].join(" "),
{ encoding: "utf8" }
);
if (result) {
if (!silent) {
console.debug(
`${LOG_PREFIX} / SUCCESS] Successfully created / updated reg key ${relativePath}/${keyName} with data: ${keyVal}`,
result
);
console.error("");
}
}
return {
success: true,
error: null,
};
} catch (e) {
console.error(
`${LOG_PREFIX_FUNC} / ERROR] Failed to create / update reg key error:`,
e
);
return {
success: false,
error: e,
};
}
}
/**
* @param {string} relativePath
* @param {string | null} keyName
* @param {boolean | undefined} silent
* @returns {Promise<{ success: boolean, error: Error | null }>}
*/
async delRegKey(relativePath, keyName, silent = false) {
if (keyName === undefined) {
throw new Error(
`${LOG_PREFIX_FUNC} / CRITICAL] Arg \"keyName\" for function \"delRegKey\" cannot be undefined. Only null or null accepted.`
);
}
try {
const { stdout } = await new Promise((resolve, reject) => {
exec(
[
"reg",
"delete",
[AURA_REGISTRY_PATH, relativePath].join("\\"),
keyName ? "/v" : "",
keyName ? keyName : "",
"/f",
].join(" "),
{ encoding: "utf8" },
(error, stdout, stderr) => {
if (error) reject(error);
else resolve({ stdout, stderr });
}
);
});
if (!silent) {
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully deleted reg key ${relativePath}/${keyName}`
);
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Delete key command stdout:`,
stdout
);
}
return {
success: true,
error: null,
};
} catch (e) {
console.error(
`${LOG_PREFIX_FUNC} / ERROR] Failed to delete reg key, error:`,
silent ? "<Hidden>" : e
);
return {
success: false,
error: e,
};
}
}
/**
* @param {string} relativePath
* @param {string | null} keyName
* @param {boolean | undefined} silent
* @returns {{ success: boolean, error: Error | null }}
*/
delRegKeySync(relativePath, keyName, silent = false) {
if (keyName === undefined) {
throw new Error(
`${LOG_PREFIX_FUNC} / CRITICAL] Arg \"keyName\" for function \"delRegKeySync\" cannot be undefined. Only null or string accepted.`
);
}
try {
const result = execSync(
[
"reg",
"delete",
[AURA_REGISTRY_PATH, relativePath].join("\\"),
keyName ? "/v" : "",
keyName ? keyName : "",
"/f",
].join(" "),
{ encoding: "utf8" }
);
if (result) {
if (!silent) {
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully deleted reg key ${relativePath}/${keyName}`
);
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Delete key command stdout:`,
result
);
}
return {
success: true,
error: null,
};
} else {
return {
success: false,
error: null,
};
}
} catch (e) {
console.error(
`${LOG_PREFIX_FUNC} / ERROR] Failed to delete reg key, error:`,
silent ? "<Hidden>" : e
);
return {
success: false,
error: e,
};
}
}
/**
* @param {string} keyPath
* @param {string} keyName
* @param {boolean | undefined} silent
* @param {boolean} absolute
* @param {any} regex
* @returns {Promise<{ success: boolean, data: string | null, error: Error | null }>}
*/
async readRegKey(
keyPath,
keyName,
silent = false,
absolute = false,
regex = null
) {
try {
const { stdout } = await new Promise((resolve, reject) => {
exec(
[
"reg",
"query",
absolute ? keyPath : [AURA_REGISTRY_PATH, keyPath].join("\\"),
"/v",
`\"${keyName}\"`,
].join(" "),
{ encoding: "utf8" },
(error, stdout, stderr) => {
if (error) reject(error);
else resolve({ stdout, stderr });
}
);
});
if (!silent) {
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${keyPath}/${keyName}, stdout:`,
stdout
);
}
const match = regex ? stdout.match(regex) : stdout.match(/REG_SZ\s+(.+)/);
if (!match) {
console.warn(`${LOG_PREFIX} / WARN] Data not found in stdout`);
return {
success: false,
data: null,
error: new Error("Data not found"),
};
}
const data = match[1].trim();
return {
success: true,
data,
error: null,
};
} catch (e) {
console.error(`${LOG_PREFIX} / ERROR] Failed to read reg key, error:`, e);
return {
success: false,
data: null,
error: e,
};
}
}
/**
* @param {string} keyPath
* @param {string} keyName
* @param {boolean | undefined} silent
* @param {boolean} absolute
* @param {any} regex
* @returns {{ success: boolean, data: string | null, error: Error | null }}
*/
readRegKeySync(
keyPath,
keyName,
silent = false,
absolute = false,
regex = null
) {
try {
const readResult = execSync(
[
"reg",
"query",
absolute ? keyPath : [AURA_REGISTRY_PATH, keyPath].join("\\"),
"/v",
`\"${keyName}\"`,
].join(" "),
{ encoding: "utf8" }
);
if (readResult) {
if (!silent) {
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${keyPath}/${keyName}, stdout:`,
readResult
);
}
const match = regex
? readResult.match(regex)
: readResult.match(/REG_SZ\s+(.+)/);
if (!match) {
console.warn(`${LOG_PREFIX} / WARN] Data not found in stdout`);
return {
success: false,
data: null,
error: new Error("Data not found"),
};
}
const data = match[1].trim();
return {
success: true,
data,
error: null,
};
} else {
return {
success: false,
data: null,
error: null,
};
}
} catch (e) {
console.error(
`${LOG_PREFIX} / ERROR] Failed to read reg key, error:`,
silent ? "<Hidden>" : e
);
return {
success: false,
data: null,
error: e,
};
}
}
}
module.exports = RegistryManager;

0
src/aura/init/zeron/hookWS.js Normal file → Executable file
View File

0
src/aura/jsRewrite/network/disableBehaviorAudit.js Normal file → Executable file
View File

0
src/aura/jsRewrite/network/disableFriday.js Normal file → Executable file
View File

0
src/aura/jsRewrite/vendor/passwordValidation.js vendored Normal file → Executable file
View File

1356
src/aura/jsRewrite/vendor/screenLock.js vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
const hookFn = (central, appIns, browserWindowIns) => {
const __config = global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"];
const removeKeyboardHook = () => {
const { dllForHookBoard } = central(29);
setTimeout(() => {
dllForHookBoard.UnHookKeyBoard();
}, 1000);
};
if (__config.disableKeyboardHook) {
removeKeyboardHook();
}
};
module.exports = { hookFunc: hookFn };

26
src/aura/types/main/core.d.ts vendored Executable file
View File

@@ -0,0 +1,26 @@
import { WebContents } from "electron";
type SeewoHugoCentralLambda = any;
type SeewoHugoGlobalConfig = Record<any, any>;
type WindowName = string;
interface LauncherArgs {
central: SeewoHugoCentralLambda;
windowName: WindowName;
config: SeewoHugoGlobalConfig;
}
interface HookedWindow {
webContents: WebContents;
domReadyListener: any;
destroyedListener: any;
}
type HookedWindowsMap = Map<WindowName, HookedWindow>;
type HookRequire = any;
type UIHooksMap = Map<WindowName, HookRequire>;
type WindowHooksMap = Map<WindowName, HookRequire>;

10
src/aura/types/main/electron.d.ts vendored Executable file
View File

@@ -0,0 +1,10 @@
import { IpcMain, WebContents } from "electron";
interface AuraIPCMain extends IpcMain {
send: (
windowKey: string,
channel: string,
data: any,
grep?: WebContents
) => void;
}

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

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

17
src/aura/types/render/global.d.ts vendored Executable file
View File

@@ -0,0 +1,17 @@
interface HugoAuraGlobal {
utils: Record<any, any>;
}
interface AssistantHugoAuraGlobal extends HugoAuraGlobal {
plsStatus: PLSStatus;
plsRules: Record<any, any>;
plsSettings: Record<any, any>;
}
interface AuraWSKeepAliveWindowHugoAuraGlobal extends HugoAuraGlobal {
plsWs: WebSocket | null;
plsStats: PLSStatus;
}
type UIFunctionsObject = Record<string, any>;
type UIReactivesObject = Record<string, any>;

28
src/aura/types/render/uiHook.d.ts vendored Executable file
View File

@@ -0,0 +1,28 @@
import { WindowName } from "../main/core";
interface UIHookTarget {
active?: boolean;
pageURI?: string;
pageScript?: string;
pageSelector?: string;
selectorMode?: "insertAfter" | "insertBefore" | "appendChild";
pageCSS?: string;
childs?: Record<AuraElementUID, UIHookTarget>;
revive?: boolean;
}
type AuraElementUID = string;
type OnLoadedEvalJS = string;
interface UIHookConfig {
targets: Record<AuraElementUID, UIHookTarget>;
globalStyles: string[];
globalJS: string[];
onLoaded: OnLoadedEvalJS;
}
interface UIHookConfigFin extends UIHookConfig {
windowName: WindowName;
}
type UIHooksObject = Record<AuraElementUID, UIHookConfigFin>;

12
src/aura/types/shared/config.d.ts vendored Executable file
View File

@@ -0,0 +1,12 @@
type AES256EncryptedConfig = string;
type Base64String = string;
type SHA256EncryptedPassword = string;
interface EncryptedConfig {
content: AES256EncryptedConfig;
authTag: Base64String;
salt: Base64String;
iv: Base64String;
}
type AuraConfig = Record<any, any>;

49
src/aura/types/shared/global.d.ts vendored Executable file
View File

@@ -0,0 +1,49 @@
import { IpcRenderer } from "electron";
import type EventBus from "../../utils/eventBus";
import { HookedWindowsMap, UIHooksMap, WindowHooksMap } from "../main/core";
import { UIHooksObject } from "../render/uiHook";
import ConfigManager from "../../init/shared/configManager";
import { PLSStatus } from "./pls/status";
type MainProcessOnlyVal<T> = T;
type RendererProcessOnlyVal<T> = T;
interface GlobalHugoAuraInfo {
central?: MainProcessOnlyVal<(...args: any) => any>;
configInit: boolean;
fsTasks?: MainProcessOnlyVal<FSTasks>;
hookedWindows?: MainProcessOnlyVal<HookedWindowsMap>;
ipcInit?: MainProcessOnlyVal<boolean>;
auraDir: MainProcessOnlyVal<string>;
plsRules?: Record<any, any> | null;
plsSettings?: Record<any, any> | null;
plsStats?: PLSStatus | null;
plsWs?: RendererProcessOnlyVal<WebSocket>;
uiHooks?: MainProcessOnlyVal<UIHooksMap>;
windowHooks?: MainProcessOnlyVal<WindowHooksMap>;
version: RendererProcessOnlyVal<string>;
}
interface GlobalHugoAuraApiInfo {
domains: string[];
plsUpdate: string;
auraUpdate: string;
}
type GlobalHugoAuraConfig = AuraConfig;
declare global {
var ipcRenderer: RendererProcessOnlyVal<IpcRenderer>;
var _ACCEPT_DATA: any;
var __HUGO_AURA__: GlobalHugoAuraInfo;
var __HUGO_AURA_API__: GlobalHugoAuraApiInfo;
var __HUGO_AURA_CONFIG__: GlobalHugoAuraConfig;
var __HUGO_AURA_CONFIG_MGR__: ConfigManager;
var __HUGO_AURA_EVENT_BUS__: EventBus;
var __HUGO_AURA_DEBUG__: RendererProcessOnlyVal<Record<any, any>>;
var __HUGO_AURA_GLOBAL__: RendererProcessOnlyVal<Record<any, any>>;
var __HUGO_AURA_HOOK__: RendererProcessOnlyVal<Record<any, any>>;
var __HUGO_AURA_LOADER__: RendererProcessOnlyVal<UIHooksObject>;
var __HUGO_AURA_UI_FUNCTIONS__: RendererProcessOnlyVal<UIFunctionsObject>;
var __HUGO_AURA_UI_REACTIVES__: RendererProcessOnlyVal<UIReactivesObject>;
}

28
src/aura/types/shared/pls/status.d.ts vendored Executable file
View File

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

18
src/aura/types/shared/pls/websocket.d.ts vendored Executable file
View File

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

0
src/aura/ui/bootstrap/bootstrap.bundle.min.js vendored Normal file → Executable file
View File

0
src/aura/ui/bootstrap/bootstrap.min.css vendored Normal file → Executable file
View File

View File

@@ -0,0 +1,81 @@
// @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,
};

555
src/aura/ui/composables/settingsRenderer.js Normal file → Executable file
View File

@@ -18,12 +18,401 @@ const showRelaunchToast = () => {
if (!toastBs.isShown()) toastBs.show();
};
const showRelaunchPLSToast = () => {
const toast = document.getElementById("relaunchPlsNotifyToast");
const toastBs = bootstrap.Toast.getOrCreateInstance(toast);
if (global.__HUGO_AURA__.plsStats.detached) {
const relaunchBtn = document.getElementById("plsRelaunchBtn");
relaunchBtn.disabled = true;
relaunchBtn.textContent = "分离模式下无法执行";
}
if (!toastBs.isShown()) toastBs.show();
};
const showToast = (entry) => {
if (entry.reload) {
showReloadToast();
} else if (entry.restart) {
showRelaunchToast();
}
/*
else if (entry.restartPLS) {
showRelaunchPLSToast();
}
*/
};
const setDisableStatus = (el, isDisable, hint = null) => {
if (isDisable) {
el.classList.add("ase-operation-area-disabled");
if (hint) {
el.setAttribute("data-bs-toggle", "tooltip");
el.setAttribute("data-bs-placement", "top");
el.setAttribute("data-bs-title", hint);
const tooltipIns = bootstrap.Tooltip.getOrCreateInstance(el);
tooltipIns.enable();
}
} else {
el.setAttribute("data-bs-toggle", "tooltip");
el.setAttribute("data-bs-placement", "top");
el.setAttribute("data-bs-title", "None");
el.classList.remove("ase-operation-area-disabled");
const tooltipIns = bootstrap.Tooltip.getInstance(el);
if (tooltipIns) {
tooltipIns.dispose();
}
}
};
const insertOrRemoveEl = (parent, child, isInsert = true) => {
if (Array.isArray(child)) {
for (const perEl of child) {
isInsert ? parent.appendChild(perEl) : parent.removeChild(perEl);
}
} else {
isInsert ? parent.appendChild(child) : parent.removeChild(child);
}
};
const createOnLeaveEvtListener =
global.__HUGO_AURA_GLOBAL__.utils.createOnLeaveEvtListener;
const renderInputArea = (entry, operationArea, descriptionArea) => {
switch (entry.type) {
case "switch": {
const switchEl = document.createElement("input");
switchEl.classList.add("form-check-input");
switchEl.type = "checkbox";
switchEl.role = "switch";
switchEl.id = entry.id;
const elValue = entry.valueGetter();
switchEl.value = elValue;
switchEl.checked = elValue;
switchEl.addEventListener("change", async (event) => {
showToast(entry);
await entry.callbackFn(
event.target.checked,
switchEl,
operationArea,
descriptionArea
);
});
operationArea.classList.add("form-check", "form-switch");
return switchEl;
}
case "radio": {
const elValue = entry.valueGetter();
const elArr = [];
for (const template of entry.templates) {
const inlineContainerEl = document.createElement("div");
inlineContainerEl.classList.add("form-check", "form-check-inline");
const radioEl = document.createElement("input");
radioEl.value = template;
radioEl.classList.add("form-check-input");
radioEl.type = "radio";
radioEl.name = `${entry.id}Radios`;
radioEl.id = `${entry.id}Radio${entry.templates.indexOf(template)}`;
radioEl.checked = template === elValue ? true : false;
radioEl.addEventListener("change", async (event) => {
if (event.target.checked) {
showToast(entry);
await entry.callbackFn(
event.target.value,
radioEl,
operationArea,
descriptionArea
);
}
});
inlineContainerEl.appendChild(radioEl);
const labelEl = document.createElement("label");
labelEl.classList.add("form-check-label");
labelEl.setAttribute("for", radioEl.id);
labelEl.textContent =
entry.templateLabels[entry.templates.indexOf(template)];
inlineContainerEl.appendChild(labelEl);
elArr.push(inlineContainerEl);
}
return elArr;
}
case "input": {
const inputEl = document.createElement("input");
inputEl.classList.add("form-control");
inputEl.type = entry.subType;
inputEl.value = entry.valueGetter();
inputEl.placeholder = entry.placeHolder;
inputEl.id = entry.id;
inputEl.addEventListener("change", async (event) => {
const result = await entry.callbackFn(
event.target.value,
inputEl,
operationArea,
descriptionArea
);
const success = result.valid;
if (success) {
showToast(entry);
if (inputEl.className.includes("is-invalid")) {
inputEl.classList.remove("is-invalid");
descriptionArea.textContent = entry.description;
descriptionArea.classList.remove("ase-desc-error-hint");
}
} else {
inputEl.classList.add("is-invalid");
descriptionArea.textContent = result.hint;
descriptionArea.classList.add("ase-desc-error-hint");
}
});
operationArea.classList.add("ase-operation-area-expanded");
return inputEl;
}
case "button": {
const btnEl = document.createElement("button");
btnEl.type = "button";
btnEl.classList.add("btn");
switch (entry.style) {
case "outline":
default:
btnEl.classList.add("btn-outline-primary");
break;
}
btnEl.innerHTML = entry.buttonContent;
btnEl.onclick = entry.callbackFn;
(async () => {
descriptionArea.innerHTML = await entry.valueGetter();
})();
return btnEl;
}
default:
break;
}
};
const renderNormalSettingsItem = (entry, formEl) => {
const entryContainerEl = document.createElement("div");
entryContainerEl.classList.add("aura-settings-entry");
entryContainerEl.id = `${entry.id}Container`;
const entryInfoContainerEl = document.createElement("div");
entryInfoContainerEl.classList.add("aura-settings-entry-info-container");
const entryTitle = document.createElement("p");
entryTitle.classList.add("aura-settings-entry-title");
entryTitle.textContent = entry.name;
if (entry.restart) {
const powerIcon = document.createElement("i");
powerIcon.classList.add(
"layui-icon",
"layui-icon-logout",
"aura-settings-entry-property-icon"
);
powerIcon.setAttribute("data-bs-toggle", "tooltip");
powerIcon.setAttribute("data-bs-placement", "top");
powerIcon.setAttribute("data-bs-title", "需要重启 Electron 进程");
entryTitle.appendChild(powerIcon);
}
if (entry.PLSRequired) {
const plsIcon = document.createElement("i");
plsIcon.classList.add(
"layui-icon",
"layui-icon-component",
"aura-settings-entry-property-icon"
);
plsIcon.setAttribute("data-bs-toggle", "tooltip");
plsIcon.setAttribute("data-bs-placement", "top");
plsIcon.setAttribute("data-bs-title", "需要 PLS 支持");
entryTitle.appendChild(plsIcon);
}
if (entry.restartPLS) {
const plsIcon = document.createElement("i");
plsIcon.classList.add(
"layui-icon",
"layui-icon-logout",
"aura-settings-entry-property-icon"
);
plsIcon.setAttribute("data-bs-toggle", "tooltip");
plsIcon.setAttribute("data-bs-placement", "top");
plsIcon.setAttribute("data-bs-title", "需要重启 PLS 进程");
entryTitle.appendChild(plsIcon);
}
if (entry.reload) {
const reloadIcon = document.createElement("i");
reloadIcon.classList.add(
"layui-icon",
"layui-icon-refresh",
"aura-settings-entry-property-icon"
);
reloadIcon.setAttribute("data-bs-toggle", "tooltip");
reloadIcon.setAttribute("data-bs-placement", "top");
reloadIcon.setAttribute("data-bs-title", "需要重载页面");
entryTitle.appendChild(reloadIcon);
}
const createToolTipIcon = (type, content) => {
const tipIcon = document.createElement("i");
tipIcon.classList.add(
"layui-icon",
"layui-icon-tips",
"aura-settings-entry-property-icon"
);
if (type === "warning") {
tipIcon.classList.add("aura-settings-entry-warning-icon");
}
tipIcon.setAttribute("data-bs-toggle", "tooltip");
tipIcon.setAttribute("data-bs-placement", "top");
tipIcon.setAttribute("data-bs-title", content);
entryTitle.appendChild(tipIcon);
};
if (entry.tip) {
createToolTipIcon("tip", entry.tipTitle);
}
if (entry.warning) {
createToolTipIcon("warning", entry.warningContent);
}
const entryDescription = document.createElement("p");
entryDescription.classList.add("aura-settings-entry-desc");
entryDescription.innerHTML = entry.description;
entryInfoContainerEl.appendChild(entryTitle);
entryInfoContainerEl.appendChild(entryDescription);
entryContainerEl.appendChild(entryInfoContainerEl);
const entryOperationArea = document.createElement("div");
entryOperationArea.classList.add("aura-settings-entry-operation-area");
let targetEl = renderInputArea(entry, entryOperationArea, entryDescription);
insertOrRemoveEl(entryOperationArea, targetEl, true);
if (entry.reactive) {
const evtListener = (event) => {
if (entry.reactiveVal.includes(event.detail.path.join("."))) {
insertOrRemoveEl(entryOperationArea, targetEl, false);
targetEl = renderInputArea(entry, entryOperationArea, entryDescription);
insertOrRemoveEl(entryOperationArea, targetEl, true);
}
};
const channel = entry.PLSRequired
? "onPLSConfigUpdate"
: "onHugoAuraConfigUpdate";
entryContainerEl.addEventListener(channel, evtListener);
// createOnLeaveEvtListener(channel, evtListener);
}
if (entry.PLSRequired) {
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();
setDisableStatus(
entryOperationArea,
isDisabledRet.value,
isDisabledRet.tooltip
);
};
if (entry.auraDisable) {
updateDisableStatus();
}
if (entry.associateVal) {
const evtListener = (event) => {
if (!entry.associateVal.includes(event.detail.path.join("."))) return;
const cls = entryContainerEl.classList;
const isShow = entry.auraIf();
isShow
? cls.remove("aura-settings-entry-hidden")
: cls.add("aura-settings-entry-hidden");
if (entry.auraDisable) {
updateDisableStatus();
}
};
const channel = entry.PLSRequired
? "onPLSConfigUpdate"
: "onHugoAuraConfigUpdate";
entryContainerEl.addEventListener(channel, evtListener);
// createOnLeaveEvtListener(channel, evtListener);
}
formEl.appendChild(entryContainerEl);
};
const renderPreviewItem = (entry, formEl) => {
const elementId = entry.customId ? entry.customId : `${entry.id}Container`;
const eventChannel = entry.listenerType;
const separateHrContainer = document.createElement("div");
separateHrContainer.classList.add("aura-settings-preview-area-hr-container");
const hrTitle = document.createElement("p");
hrTitle.textContent = "预览";
const hrElement = document.createElement("hr");
hrElement.classList.add("aura-settings-preview-hr");
separateHrContainer.appendChild(hrElement);
separateHrContainer.appendChild(hrTitle);
separateHrContainer.appendChild(hrElement.cloneNode());
formEl.appendChild(separateHrContainer);
const previewContainerEl = document.createElement("div");
previewContainerEl.classList.add("aura-settings-preview-area-container");
previewContainerEl.id = elementId;
const eventListener = (event) => {
const childs = previewContainerEl.querySelectorAll("*");
Array.from(childs).forEach((el) => {
el.dispatchEvent(
new CustomEvent("onAssociateValueUpdated", { detail: event.detail })
);
});
};
document.addEventListener(
eventChannel === "pls" ? "onPLSConfigUpdate" : "onHugoAuraConfigUpdate",
eventListener
);
createOnLeaveEvtListener(eventListener); // Clean up
formEl.appendChild(previewContainerEl);
setTimeout(() => {
global.__HUGO_AURA_LOADER__[entry.loaderTarget].active = true;
}, 50);
};
const renderChild = (entry, formEl) => {
switch (entry.type) {
case "preview":
renderPreviewItem(entry, formEl);
break;
default:
renderNormalSettingsItem(entry, formEl);
break;
}
};
const settingsRenderer = (pendingEl, settingsObj) => {
@@ -35,164 +424,7 @@ const settingsRenderer = (pendingEl, settingsObj) => {
categoryTitleEl.textContent = category.categoryName;
formEl.appendChild(categoryTitleEl);
for (const entry of category.child) {
const entryContainerEl = document.createElement("div");
entryContainerEl.classList.add("aura-settings-entry");
entryContainerEl.id = `${entry.id}Container`;
const entryInfoContainerEl = document.createElement("div");
entryInfoContainerEl.classList.add("aura-settings-entry-info-container");
const entryTitle = document.createElement("p");
entryTitle.classList.add("aura-settings-entry-title");
entryTitle.textContent = entry.name;
if (entry.restart) {
const powerIcon = document.createElement("i");
powerIcon.classList.add(
"layui-icon",
"layui-icon-logout",
"aura-settings-entry-property-icon"
);
powerIcon.setAttribute("data-bs-toggle", "tooltip");
powerIcon.setAttribute("data-bs-placement", "top");
powerIcon.setAttribute("data-bs-title", "需要重启 Electron 进程");
entryTitle.appendChild(powerIcon);
}
if (entry.reload) {
const reloadIcon = document.createElement("i");
reloadIcon.classList.add(
"layui-icon",
"layui-icon-refresh",
"aura-settings-entry-property-icon"
);
reloadIcon.setAttribute("data-bs-toggle", "tooltip");
reloadIcon.setAttribute("data-bs-placement", "top");
reloadIcon.setAttribute("data-bs-title", "需要重载页面");
entryTitle.appendChild(reloadIcon);
}
if (entry.tip) {
const tipIcon = document.createElement("i");
tipIcon.classList.add(
"layui-icon",
"layui-icon-tips",
"aura-settings-entry-property-icon"
);
tipIcon.setAttribute("data-bs-toggle", "tooltip");
tipIcon.setAttribute("data-bs-placement", "top");
tipIcon.setAttribute("data-bs-title", entry.tipTitle);
entryTitle.appendChild(tipIcon);
}
const entryDescription = document.createElement("p");
entryDescription.classList.add("aura-settings-entry-desc");
entryDescription.textContent = entry.description;
entryInfoContainerEl.appendChild(entryTitle);
entryInfoContainerEl.appendChild(entryDescription);
entryContainerEl.appendChild(entryInfoContainerEl);
const entryOperationArea = document.createElement("div");
entryOperationArea.classList.add("aura-settings-entry-operation-area");
switch (entry.type) {
case "switch":
{
const switchEl = document.createElement("input");
switchEl.classList.add("form-check-input");
switchEl.type = "checkbox";
switchEl.role = "switch";
switchEl.id = entry.id;
const elValue = entry.valueGetter();
switchEl.value = elValue;
switchEl.checked = elValue;
switchEl.addEventListener("change", (event) => {
showToast(entry);
entry.callbackFn(event.target.checked);
});
entryOperationArea.classList.add("form-check", "form-switch");
entryOperationArea.appendChild(switchEl);
}
break;
case "radio":
{
const elValue = entry.valueGetter();
for (const template of entry.templates) {
const inlineContainerEl = document.createElement("div");
inlineContainerEl.classList.add(
"form-check",
"form-check-inline"
);
const radioEl = document.createElement("input");
radioEl.value = template;
radioEl.classList.add("form-check-input");
radioEl.type = "radio";
radioEl.name = `${entry.id}Radios`;
radioEl.id = `${entry.id}Radio${entry.templates.indexOf(
template
)}`;
radioEl.checked = template === elValue ? true : false;
radioEl.addEventListener("change", (event) => {
if (event.target.checked) {
showToast(entry);
entry.callbackFn(event.target.value);
}
});
inlineContainerEl.appendChild(radioEl);
const labelEl = document.createElement("label");
labelEl.classList.add("form-check-label");
labelEl.setAttribute("for", radioEl.id);
labelEl.textContent =
entry.templateLabels[entry.templates.indexOf(template)];
inlineContainerEl.appendChild(labelEl);
entryOperationArea.appendChild(inlineContainerEl);
}
}
break;
case "input":
{
const inputEl = document.createElement("input");
inputEl.classList.add("form-control");
inputEl.type = entry.subType;
inputEl.value = entry.valueGetter();
inputEl.placeholder = entry.placeHolder;
inputEl.id = entry.id;
inputEl.addEventListener("change", (event) => {
const result = entry.callbackFn(event.target.value);
const success = result.valid;
if (success) {
showToast(entry);
if (inputEl.className.includes("is-invalid")) {
inputEl.classList.remove("is-invalid");
entryDescription.textContent = entry.description;
entryDescription.classList.remove("ase-desc-error-hint");
}
} else {
inputEl.classList.add("is-invalid");
entryDescription.textContent = result.hint;
entryDescription.classList.add("ase-desc-error-hint");
}
});
entryOperationArea.classList.add("ase-operation-area-expanded");
entryOperationArea.appendChild(inputEl);
}
break;
default:
break;
}
entryContainerEl.appendChild(entryOperationArea);
const isShow = entry.auraIf();
if (!isShow) entryContainerEl.classList.add("aura-settings-entry-hidden");
if (entry.associateVal) {
document.addEventListener("onHugoAuraConfigUpdate", (event) => {
if (!entry.associateVal.includes(event.detail.path.join("."))) return;
const cls = entryContainerEl.classList;
const isShow = entry.auraIf();
isShow
? cls.remove("aura-settings-entry-hidden")
: cls.add("aura-settings-entry-hidden");
});
}
formEl.appendChild(entryContainerEl);
renderChild(entry, formEl);
}
const hrEl = document.createElement("hr");
@@ -201,11 +433,8 @@ const settingsRenderer = (pendingEl, settingsObj) => {
}
pendingEl.appendChild(formEl);
const tooltipTriggerList = document.querySelectorAll(
'[data-bs-toggle="tooltip"]'
);
[...tooltipTriggerList].map(
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
global.__HUGO_AURA_GLOBAL__.utils.refreshBsTooltip(
".aura-settings-entry-property-icon"
);
};

0
src/aura/ui/css/assistant.css Normal file → Executable file
View File

68
src/aura/ui/css/form.css Normal file → Executable file
View File

@@ -46,6 +46,15 @@
flex: 0.75;
}
.aura-settings-entry-operation-area.ase-operation-area-disabled {
opacity: 0.25;
cursor: not-allowed;
}
.aura-settings-entry-operation-area.ase-operation-area-disabled * {
pointer-events: none;
}
.aura-settings-entry-operation-area.form-switch {
transform: scale(1.2);
}
@@ -56,6 +65,33 @@
border-bottom: 0.75px solid rgba(0, 0, 0, 0.25);
}
.aura-settings-preview-area-hr-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.aura-settings-preview-hr {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
border-bottom: 0.75px solid rgba(0, 0, 0, 0.25);
width: -webkit-fill-available;
}
.aura-settings-preview-area-hr-container p {
margin-left: 0.5rem;
margin-right: 0.5rem;
min-width: 2rem;
opacity: 0.75;
text-align: center;
}
.aura-settings-preview-area-container {
width: 100%;
padding-bottom: 0.5rem;
}
.aura-settings-entry-property-icon {
margin-left: 0.5rem;
font-size: 15px;
@@ -68,3 +104,35 @@
.aura-settings-entry-property-icon.layui-icon-refresh {
color: rgb(0, 106, 188);
}
.aura-settings-entry-warning-icon {
transform: rotate(180deg);
display: inline-block;
color: rgb(147, 0, 0);
}
/* Animations */
@keyframes invalidShake {
0% {
margin-left: calc(-10px * 2);
}
16% {
margin-left: calc(9px * 2);
}
33% {
margin-left: calc(-6px * 2);
}
50% {
margin-left: calc(5px * 2);
}
66% {
margin-left: calc(-2px * 2);
}
83% {
margin-left: calc(1px * 2);
}
100% {
margin-left: calc(0px * 2);
}
}

4
src/aura/ui/css/global.css Normal file → Executable file
View File

@@ -25,3 +25,7 @@ button.nav-link {
.nav-underline {
--bs-nav-underline-link-active-color: #0d6efd !important;
}
.tooltip {
--bs-tooltip-max-width: 400px !important;
}

View File

@@ -0,0 +1,97 @@
// @ts-check
/**
* @type {import("../../types/render/uiHook").UIHookConfig}
*/
const def = {
targets: {
"Aura.UI.Assistant.HeaderEntry": {
active: true,
pageURI: "ui/pages/headerIcon/headerIcon.html",
pageScript: "ui/pages/headerIcon/headerIcon.js",
pageSelector: ".index__feedback__2XvUK2qe",
selectorMode: "insertAfter",
pageCSS: "ui/pages/headerIcon/headerIcon.css",
revive: true,
},
"Aura.UI.Assistant.Config": {
active: false,
pageURI: "ui/pages/config/config.html",
pageScript: "ui/pages/config/config.js",
pageSelector: "#root",
selectorMode: "appendChild",
pageCSS: "ui/pages/config/config.css",
childs: {
DisableLimitations: {
active: false,
pageURI:
"ui/pages/configSubPages/disableLimitations/disableLimitations.html",
pageScript:
"ui/pages/configSubPages/disableLimitations/disableLimitations.js",
pageSelector: ".aura-config-page-subpage-container",
selectorMode: "appendChild",
pageCSS:
"ui/pages/configSubPages/disableLimitations/disableLimitations.css",
},
BehaviourCtrl: {
active: false,
pageURI: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.html",
pageScript: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.js",
pageSelector: ".aura-config-page-subpage-container",
selectorMode: "appendChild",
pageCSS: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.css",
childs: {
PlsStatus: {
active: false,
pageURI: "ui/pages/configSubPages/behaviourCtrl/plsStatus.html",
pageScript: "ui/pages/configSubPages/behaviourCtrl/plsStatus.js",
pageSelector: "#status-subpage",
selectorMode: "appendChild",
pageCSS: "ui/pages/configSubPages/behaviourCtrl/plsStatus.css",
},
DeviceSecurity: {
childs: {
FreezeOverridePreview: {
active: false,
pageURI:
"ui/pages/configSubPages/behaviourCtrl/settings/previews/freezeOverridePreview/freezeOverridePreview.html",
pageScript:
"ui/pages/configSubPages/behaviourCtrl/settings/previews/freezeOverridePreview/freezeOverridePreview.js",
pageSelector: "#freezeInfoReportOverridePreviewContainer",
selectorMode: "appendChild",
pageCSS:
"ui/pages/configSubPages/behaviourCtrl/settings/previews/freezeOverridePreview/freezeOverridePreview.css",
},
},
},
},
},
Preferences: {
active: false,
pageURI: "ui/pages/configSubPages/preferences/preferences.html",
pageScript: "ui/pages/configSubPages/preferences/preferences.js",
pageSelector: ".aura-config-page-subpage-container",
selectorMode: "appendChild",
pageCSS: "ui/pages/configSubPages/preferences/preferences.css",
},
},
},
},
globalStyles: [
"ui/css/global.css",
"ui/css/assistant.css",
"ui/css/form.css",
"ui/layui/css/layui.css",
"ui/bootstrap/bootstrap.min.css",
],
globalJS: [
"ui/js/global.js",
"ui/js/plsListener.js",
"ui/bootstrap/bootstrap.bundle.min.js",
],
onLoaded: `
console.log('[HugoAura / UI / Hooks / Assistant] Page loaded.');
`,
};
module.exports = def;

View File

@@ -0,0 +1,15 @@
// @ts-check
/**
* @type {import("../../types/render/uiHook").UIHookConfig}
*/
const def = {
targets: {},
globalStyles: ["ui/css/global.css"],
globalJS: ["ui/js/global.js", "ui/js/pageGlobal/desktopAssistant.js"],
onLoaded: `
console.log('[HugoAura / UI / Hooks / Desktop Assistant] Page loaded.');
`,
};
module.exports = def;

View File

@@ -1,43 +0,0 @@
module.exports = {
targets: {
"Aura.UI.Assistant.HeaderEntry": {
active: true,
pageURI: "ui/pages/headerIcon/headerIcon.html",
pageScript: "ui/pages/headerIcon/headerIcon.js",
pageSelector: ".index__feedback__2XvUK2qe",
selectorMode: "insertAfter",
pageCSS: "ui/pages/headerIcon/headerIcon.css",
revive: true,
},
"Aura.UI.Assistant.Config": {
active: false,
pageURI: "ui/pages/config/config.html",
pageScript: "ui/pages/config/config.js",
pageSelector: "#root",
selectorMode: "appendChild",
pageCSS: "ui/pages/config/config.css",
},
"Aura.UI.Assistant.Config.DisableLimitations": {
active: false,
pageURI:
"ui/pages/configSubPages/disableLimitations/disableLimitations.html",
pageScript:
"ui/pages/configSubPages/disableLimitations/disableLimitations.js",
pageSelector: ".aura-config-page-subpage-container",
selectorMode: "appendChild",
pageCSS:
"ui/pages/configSubPages/disableLimitations/disableLimitations.css",
},
},
globalStyles: [
"ui/css/global.css",
"ui/css/assistant.css",
"ui/css/form.css",
"ui/layui/css/layui.css",
"ui/bootstrap/bootstrap.min.css",
],
globalJS: ["ui/js/global.js", "ui/bootstrap/bootstrap.bundle.min.js"],
onLoaded: `
console.log('[HugoAura / UI / Hooks / Assistant] Page loaded.');
`,
};

32
src/aura/ui/js/global.js Normal file → Executable file
View File

@@ -1,9 +1,41 @@
(() => {
/* Util: Sleep */
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
/* Util: BootStrap Tooltip Ctrl */
let tooltipTriggerCache = null;
const refreshBsTooltip = (selector = '[data-bs-toggle="tooltip"]') => {
if (tooltipTriggerCache) {
[...tooltipTriggerCache].map((el) =>
bootstrap.Tooltip.getInstance(el).disable()
);
}
const tooltipTriggerList = document.querySelectorAll(selector);
tooltipTriggerCache = tooltipTriggerList;
[...tooltipTriggerList].map(
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
);
};
const createOnLeaveEvtListener = (
channel,
pendingRmEvtListener,
leaveEvt = "onCurConfigPageLeave"
) => {
const rmEvtListener = (event) => {
document.removeEventListener(channel, pendingRmEvtListener);
document.removeEventListener(leaveEvt, rmEvtListener);
};
document.addEventListener(leaveEvt, rmEvtListener);
return rmEvtListener;
};
if (!window.__HUGO_AURA_GLOBAL__) window.__HUGO_AURA_GLOBAL__ = {};
window.__HUGO_AURA_GLOBAL__.utils = {
sleep,
refreshBsTooltip,
createOnLeaveEvtListener,
};
})();

View File

@@ -0,0 +1,23 @@
// @ts-check
(() => {
const applyHideSettings = () => {
if (global.__HUGO_AURA_CONFIG__.ssa.ux.easiAssistant.autoHide) {
const minimizeBtnEl = document.getElementsByClassName(
"index__button2__2mhwC3oY"
)[0].children[0];
// @ts-expect-error
minimizeBtnEl.click();
} else if (global.__HUGO_AURA_CONFIG__.ssa.ux.easiAssistant.notDisplay) {
const rootEl = document.getElementById("root");
// @ts-expect-error
rootEl.style["display"] = "none";
}
};
const onMounted = () => {
applyHideSettings();
};
onMounted();
})();

View File

@@ -0,0 +1,340 @@
// @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: "未知",
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;
}
}
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: "未知",
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();
}
);
};
setTimeout(() => {
onSetup();
}, 1500);
})();

112
src/aura/ui/js/plsListener.js Executable file
View File

@@ -0,0 +1,112 @@
(() => {
const IPC_METHOD_BASE = "$aura.pls";
const REQUIRE_BASE = "../../aura/ui";
const __SCOPE = "assistant";
const {
updatePlsStatusFromLocal,
} = require(`${REQUIRE_BASE}/composables/plsConfigManager`);
const setupListeners = () => {
if (!global.ipcRenderer)
global.ipcRenderer = require("electron").ipcRenderer;
ipcRenderer.on(
`${IPC_METHOD_BASE}.post.onPlsStatsUpdate`,
(_event, arg) => {
global.__HUGO_AURA__.plsStats = arg;
const event = new CustomEvent("onPLSStatsUpdate", {
detail: {
connected: arg.connected,
},
});
document.dispatchEvent(event);
const settingsEntries = document.getElementsByClassName(
"aura-settings-entry"
);
if (settingsEntries.length > 0) {
Array.from(settingsEntries).forEach((entry) => {
entry.dispatchEvent(event);
});
}
}
);
ipcRenderer.on(
`${IPC_METHOD_BASE}.post.onPlsSettingsUpdate`,
(_event, arg) => {
global.__HUGO_AURA__.plsSettings = arg;
const event = new CustomEvent("onPLSConfigUpdate", {
detail: {
path: ["root", "settings"],
value: arg,
},
});
document.dispatchEvent(event);
const settingsEntries = document.getElementsByClassName(
"aura-settings-entry"
);
if (settingsEntries.length > 0) {
Array.from(settingsEntries).forEach((entry) => {
entry.dispatchEvent(event);
});
}
}
);
ipcRenderer.on(
`${IPC_METHOD_BASE}.post.onPlsRulesUpdate`,
(_event, arg) => {
global.__HUGO_AURA__.plsRules = arg;
const event = new CustomEvent("onPLSConfigUpdate", {
detail: {
path: ["root", "ruleSettings"],
value: arg,
},
});
document.dispatchEvent(event);
const settingsEntries = document.getElementsByClassName(
"aura-settings-entry"
);
if (settingsEntries.length > 0) {
Array.from(settingsEntries).forEach((entry) => {
entry.dispatchEvent(event);
});
}
}
);
ipcRenderer.on(
`${IPC_METHOD_BASE}.post.updateRetryStatus`,
(_event, arg) => {
const event = new CustomEvent("onPLSStatsUpdate", {
detail: {
connected: arg.success,
},
});
document.dispatchEvent(event);
if (
global.__HUGO_AURA_LOADER__["Aura.UI.Assistant.Config.BehaviourCtrl"]
.active
) {
setTimeout(() => {
global.__HUGO_AURA_GLOBAL__.utils.refreshBsTooltip();
}, 500);
}
}
);
};
updatePlsStatusFromLocal();
setupListeners();
})();

0
src/aura/ui/layui/css/layui.css Normal file → Executable file
View File

0
src/aura/ui/layui/font/iconfont.eot Normal file → Executable file
View File

0
src/aura/ui/layui/font/iconfont.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 322 KiB

After

Width:  |  Height:  |  Size: 322 KiB

0
src/aura/ui/layui/font/iconfont.ttf Normal file → Executable file
View File

0
src/aura/ui/layui/font/iconfont.woff Normal file → Executable file
View File

0
src/aura/ui/layui/font/iconfont.woff2 Normal file → Executable file
View File

400
src/aura/ui/pages/config/config.css Normal file → Executable file
View File

@@ -1,409 +1,27 @@
/* General */
#aura-container-Aura-UI-Assistant-Config {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 1000;
}
.aura-config-page-root {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: url("../../../../app.asar/public/ae247697b4639c92bd008d0ea7d13b53.png");
/* 这里不用 background-size: cover; 的效果反而更舒服一些... */
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
opacity: 1;
transform: scale(1);
transition: all 0.5s;
}
.aura-config-page-root-inactive {
opacity: 0;
transform: scale(1.5);
}
@import url("./css/general.css");
/* Header */
.aura-config-page-header-area {
flex: 1;
display: flex;
flex-direction: row;
justify-content: flex-start;
width: 100%;
padding-left: 8px;
padding-right: 8px;
color: white;
opacity: 1;
transform: translateY(0);
transition: all 0.5s;
}
.aura-config-page-header-area .iconfont {
font-size: 24px;
transition: all 0.25s;
}
.aura-config-page-header-area .iconfont:hover {
opacity: 0.75;
cursor: pointer;
}
.aura-config-page-header-area .iconfont:active {
opacity: 0.375;
}
.aura-config-page-header-area p {
margin-top: -2px;
}
.aura-config-page-header-area.header-collapsed {
transform: translateY(-1rem);
opacity: 0;
}
.aura-config-page-app-bar {
height: 40px;
display: flex;
justify-content: flex-start;
align-items: center;
width: 100%;
}
@import url("./css/header.css");
/* Status */
.aura-config-page-status-container {
flex: 1;
width: 100%;
align-self: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
opacity: 1;
transition: all 0.5s;
}
.aura-config-page-status-container-hidden {
position: absolute;
opacity: 0;
}
.aura-config-page-status-main,
.aura-config-page-status-description {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.aura-config-page-status-description {
margin-top: 0.5rem;
transform: translateY(0);
opacity: 1;
transition: all 0.5s;
}
.aura-config-page-status-description.status-description-hidden {
transform: translateY(-2rem);
opacity: 0;
}
.aura-config-page-status-description p {
font-size: 18px;
margin-left: 15px;
margin-top: -2px;
color: white;
font-family: "Consolas", "Microsoft YaHei", sans-serif;
}
.aura-config-page-status-description i {
color: white;
}
.aura-config-page-central-aura-logo {
margin: 0.5rem 3rem;
width: 17.5%;
}
.aura-config-hr-vertical {
height: 3.75rem;
width: 1px;
background-color: rgba(255, 255, 255, 0.3);
margin-left: 30px;
margin-right: 30px;
border: none;
}
.aura-config-page-status-el {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: "Consolas", monospace;
color: white;
/*
.version-type {
content: "I want to use scss plz 😇"
}
*/
}
.aura-config-page-status-side {
height: 30%;
display: flex;
flex-direction: row;
align-items: center;
flex: 1;
transform: translateX(0);
opacity: 1;
transition: transform 0.5s, opacity 0.5s;
}
.aura-config-page-status-side.left-side {
justify-content: flex-end;
}
.aura-config-page-status-side.left-side.status-side-hidden {
transform: translateX(5rem);
opacity: 0;
}
.aura-config-page-status-side.right-side {
justify-content: flex-start;
}
.aura-config-page-status-side.right-side.status-side-hidden {
transform: translateX(-5rem);
opacity: 0;
}
.aura-config-page-status-el .version-type {
font-size: 20px;
font-weight: 500;
}
.aura-config-page-status-el .version-content {
font-size: 16px;
margin-top: 5px;
opacity: 0.625;
}
@import url("./css/status.css");
/* Operation */
.aura-config-page-operation-area {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
flex: 1;
width: 100%;
overflow-y: auto;
}
@import url("./css/operation.css");
.aura-config-page-operation-area::-webkit-scrollbar {
display: none;
}
/* Config Status Notify */
.aura-config-page-operation-area.subpage-expanded {
flex: 15;
}
@import url("./css/configStatusNotify.css");
.aura-config-page-subpage-container {
width: 100%;
height: 0;
background-color: rgba(255, 255, 255, 0.825);
z-index: 6000;
overflow-y: scroll;
/* Auth Dialog */
opacity: 0;
transition: all 0.5s;
}
.aura-config-page-subpage-container::-webkit-scrollbar {
display: none;
}
.aura-config-page-operation-area.subpage-expanded
.aura-config-page-subpage-container {
height: calc(100% - 40px - 4rem);
opacity: 1;
}
.aura-config-page-operation-container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
background-color: rgba(255, 255, 255, 0.1);
padding-left: 1rem;
padding-right: 1rem;
}
.aura-config-page-operation-container.hide-other-operations
.aura-config-page-operation-el:not(.preserve-operation) {
max-width: 0;
opacity: 0;
}
.aura-config-page-operation-container.hide-other-operations
.aura-config-page-operation-el.preserve-operation {
flex: 0.25;
}
.aura-config-page-operation-el {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex: 1;
padding-top: 2rem;
padding-bottom: 2rem;
overflow: hidden;
white-space: nowrap;
max-width: 25%;
opacity: 1;
transform: translateY(0);
transition: opacity 0.5s, transform 0.5s,
max-width cubic-bezier(0, 0.42, 0.18, 1) 0.5s;
}
.aura-config-page-operation-el.operation-el-show:hover {
cursor: pointer;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover {
cursor: not-allowed;
}
.aura-config-page-operation-el.operation-el-hidden {
transform: translateY(2rem);
opacity: 0;
}
.aura-config-page-operation-el.operation-el-show
.aura-config-page-operation-body {
opacity: 1;
transition: opacity 0.25s;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]
.aura-config-page-operation-body {
transition: opacity 0.5s;
}
.aura-config-page-operation-el.operation-el-show:not(.preserve-operation):hover
.aura-config-page-operation-body {
opacity: 0.625;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover
.aura-config-page-operation-body {
opacity: 0.25;
}
.aura-config-page-operation-el.operation-el-show:not(.preserve-operation):active
.aura-config-page-operation-body {
opacity: 0.25;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]::after {
content: "别急嘛, 还在开发呢...";
font-size: 16px;
opacity: 0;
color: white;
position: absolute;
transition: all 0.5s;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover::after,
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:active::after {
opacity: 1;
}
.aura-config-page-operation-body {
display: flex;
align-items: center;
justify-content: center;
}
.aura-config-page-operation-el img {
max-width: 40px;
margin-right: 20px;
}
.aura-config-page-operation-el .config-operation-title {
color: white;
font-size: large;
}
.aura-config-page-operation-el .config-operation-description {
color: white;
opacity: 0.75;
font-size: small;
}
@import url("./css/authDialog.css");
/* Toast */
.aura-config-page-toast-area {
z-index: 9000;
}
.aura-config-page-toast-area .toast {
--bs-toast-border-width: 0 !important;
--bs-toast-bg: #fff !important;
}
.aura-config-page-toast-area .toast-header {
background-color: rgb(255, 234, 202);
border-top-left-radius: var(--bs-toast-border-radius);
border-top-right-radius: var(--bs-toast-border-radius);
}
.aura-config-page-toast-area .toast.acp-toast-emerg .toast-header {
background-color: rgb(255, 202, 202);
}
.aura-config-page-toast-area .toast-header * {
color: rgba(234, 126, 14, 0.85);
}
.aura-config-page-toast-area .toast.acp-toast-emerg .toast-header * {
color: rgba(234, 65, 14, 0.85);
}
.aura-config-page-toast-area .toast-body p {
margin-bottom: var(--bs-toast-padding-x);
}
.aura-config-page-toast-area .toast-header .layui-icon {
font-weight: bolder;
margin-right: 0.5rem;
font-size: 18px;
}
@import url("./css/toast.css");

178
src/aura/ui/pages/config/config.html Normal file → Executable file
View File

@@ -1,13 +1,60 @@
<div class="aura-config-page-root-inactive aura-config-page-root">
<div
class="aura-config-page-root-inactive aura-config-page-root"
style="display: none"
>
<!-- display: none 用于防止 CSS 还未加载完成时, 用户看到错乱的样式 -->
<div class="header-collapsed aura-config-page-header-area">
<div class="aura-config-page-app-bar" style="-webkit-app-region: drag">
<div
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavBack()"
style="-webkit-app-region: no-drag; z-index: 2000"
style="-webkit-app-region: no-drag; z-index: 2000; margin-right: 0.1rem"
>
<i class="iconfont"></i>
<!-- Chevron Left Icon -->
</div>
<p>雨光之环</p>
<div class="aura-config-page-app-bar-hr-vertical" id="auraConfigPageAppBarVerticalHr"></div>
<div class="aura-config-page-app-bar-spacer space-none"></div>
<div
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavHome()"
style="
-webkit-app-region: no-drag;
z-index: 2000;
margin-left: 6px;
margin-right: 0.5rem;
"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="22"
height="22"
viewBox="0 0 24 24"
class="iconfont"
style="margin-top: -1.35px"
>
<path
fill="currentColor"
d="M6 19h3.692v-5.884h4.616V19H18v-9l-6-4.538L6 10zm-1 1V9.5l7-5.288L19 9.5V20h-5.692v-5.884h-2.616V20zm7-7.77"
stroke-width="0.5"
stroke="currentColor"
/>
</svg>
</div>
<div class="aura-config-page-app-bar-spacer"></div>
<div
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.minimizeWindow()"
style="-webkit-app-region: no-drag; z-index: 2000"
>
<i class="iconfont"></i>
<!-- Minimize Icon -->
</div>
<div
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.closeWindow()"
style="-webkit-app-region: no-drag; z-index: 2000; margin-left: 0.5rem"
>
<i class="iconfont"></i>
<!-- Failed / Cancel Icon -->
</div>
</div>
</div>
@@ -66,6 +113,28 @@
<div class="aura-config-page-operation-area">
<div class="aura-config-page-subpage-container"></div>
<div class="aura-config-page-operation-container">
<div class="acp-config-status-notify hidden fully-hidden">
<div class="acp-config-status-notify-area">
<div class="acp-config-status-notify-main-content">
<i class="layui-icon layui-icon-component acsn-main-icon"></i>
<p class="acsn-main-title">修改的配置暂未保存</p>
<button
type="button"
class="btn btn-outline-primary btn-sm acsn-save-btn"
onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.handleSaveConfig()"
>
保存配置
</button>
</div>
<div
class="acp-config-status-notify-success acsn-success-hidden acsn-success-fully-hidden"
>
<i class="layui-icon layui-icon-release"></i>
<p>保存成功</p>
</div>
</div>
</div>
<div
class="operation-el-hidden aura-config-page-operation-el"
onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig('disableLimitations', true)"
@@ -73,19 +142,20 @@
<div class="aura-config-page-operation-body">
<img src="../../aura/ui/static/config/no_limitations.svg" />
<div>
<p class="config-operation-title">限制解除</p>
<p class="config-operation-description">禁用密码、关闭冰点</p>
<p class="config-operation-title">行为改写</p>
<p class="config-operation-description">覆写密码、禁用能力</p>
</div>
</div>
</div>
<div
class="operation-el-hidden aura-config-page-operation-el"
aura-disabled="true"
onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig('behaviourCtrl', true)"
>
<!-- Still WIP -->
<div class="aura-config-page-operation-body">
<img src="../../aura/ui/static/config/behaviour_mon.svg" />
<div>
<p class="config-operation-title">行为管控</p>
<p class="config-operation-title">操作管控</p>
<p class="config-operation-description">窥屏提醒、数据欺骗</p>
</div>
</div>
@@ -102,18 +172,55 @@
</div>
</div>
</div>
<div class="operation-el-hidden aura-config-page-operation-el">
<div
class="operation-el-hidden aura-config-page-operation-el"
onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig('preferences', true)"
>
<div class="aura-config-page-operation-body">
<img src="../../aura/ui/static/config/about.svg" />
<div>
<p class="config-operation-title">关于项目</p>
<p class="config-operation-description">使用文档、获取帮助</p>
<p class="config-operation-title">偏好设置</p>
<p class="config-operation-description">Aura 设置、关于项目</p>
</div>
</div>
</div>
</div>
</div>
<div
class="aura-config-page-auth-dialog-area acp-ada-hidden"
style="display: none"
aura-cancel="true"
>
<div class="aura-config-page-auth-dialog">
<p class="acp-auth-dialog-title">验证您的身份</p>
<input
type="password"
class="form-control"
placeholder="请输入密码..."
aria-label="Aura Password"
id="acp-auth-user-input"
/>
<div class="acp-auth-btns-container">
<button
class="acp-auth-cancel-btn"
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavBack()"
>
<i class="layui-icon layui-icon-return"></i>
</button>
<button
class="acp-auth-confirm-btn"
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword()"
>
<i class="layui-icon layui-icon-ok"></i>
</button>
</div>
</div>
</div>
<div class="aura-config-page-toast-area">
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div
@@ -125,13 +232,22 @@
<div class="toast-header">
<i class="layui-icon layui-icon-tips"></i>
<strong class="me-auto">重载页面以应用设置</strong>
<button
type="button"
class="btn-close"
data-bs-dismiss="toast"
></button>
</div>
<div class="toast-body">
<p>请重载当前窗口以应用修改的设置</p>
<p>已修改的配置将自动保存</p>
<button
type="button"
class="btn btn-primary btn-sm"
onclick="window.location.reload()"
onclick="(() => {
global.__HUGO_AURA_CONFIG_MGR__.writeConfig(global.__HUGO_AURA_CONFIG__);
window.location.reload();
})()"
>
重载页面
</button>
@@ -149,13 +265,53 @@
<div class="toast-header">
<i class="layui-icon layui-icon-tips"></i>
<strong class="me-auto">重启进程以应用设置</strong>
<button
type="button"
class="btn-close"
data-bs-dismiss="toast"
></button>
</div>
<div class="toast-body">
<p>请重启 Electron 进程以应用修改的设置</p>
<p>已修改的配置将自动保存</p>
<button
type="button"
class="btn btn-primary btn-sm"
onclick="ipcRenderer.invoke('$aura.base.restartApplication')"
onclick="(() => {
global.__HUGO_AURA_CONFIG_MGR__.writeConfig(global.__HUGO_AURA_CONFIG__);
ipcRenderer.invoke('$aura.base.restartApplication')
})()"
>
重启进程
</button>
</div>
</div>
</div>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div
id="relaunchPlsNotifyToast"
class="toast acp-toast-emerg"
aria-atomic="true"
data-bs-autohide="false"
>
<div class="toast-header">
<i class="layui-icon layui-icon-tips"></i>
<strong class="me-auto">重启 PLS 进程以应用设置</strong>
<button
type="button"
class="btn-close"
data-bs-dismiss="toast"
></button>
</div>
<div class="toast-body">
<p>请重启 PLS 进程以应用修改的设置</p>
<p>已修改的配置将自动保存</p>
<button
type="button"
class="btn btn-primary btn-sm"
id="plsRelaunchBtn"
onclick="ipcRenderer.invoke('$aura.pls.relaunchPls')"
>
重启进程
</button>

258
src/aura/ui/pages/config/config.js Normal file → Executable file
View File

@@ -1,20 +1,64 @@
global.__HUGO_AURA_UI_REACTIVES__.config = {
isInSubPage: false,
currentActiveSubPage: "",
authenticated: false,
isConfigPendingWrite: false,
};
global.__HUGO_AURA_UI_FUNCTIONS__.config = {
closeWindow: async () => {
if (global.__HUGO_AURA_UI_REACTIVES__.config.isConfigPendingWrite) {
await global.__HUGO_AURA_UI_FUNCTIONS__.config.handleSaveConfig();
}
global.ipcRenderer.send("$aura.base.closeWindow", {
targetWindowKey: "assistant",
});
},
minimizeWindow: () => {
global.ipcRenderer.send("$aura.base.minimizeWindow", {
targetWindowKey: "assistant",
});
},
handleNavBack: () => {
if (global.__HUGO_AURA_UI_REACTIVES__.config.isInSubPage) {
const acsDialogAreaEl = document.getElementsByClassName(
"aura-config-page-auth-dialog-area"
)[0];
if (!Array.from(acsDialogAreaEl.classList).includes("acp-ada-hidden")) {
global.__HUGO_AURA_UI_FUNCTIONS__.config.hideAndResetAuthDialog();
return;
}
if (global.__HUGO_AURA_UI_REACTIVES__.config.isConfigPendingWrite) {
global.__HUGO_AURA_UI_FUNCTIONS__.config.handleSaveConfig();
}
global.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig(
global.__HUGO_AURA_UI_REACTIVES__.config.currentActiveSubPage,
false
);
const onLeaveEvent = new CustomEvent("onCurConfigPageLeave");
document.dispatchEvent(onLeaveEvent);
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.config.hideConfigPage();
}
},
handleNavHome: async () => {
if (global.__HUGO_AURA_UI_REACTIVES__.config.isConfigPendingWrite) {
global.__HUGO_AURA_UI_FUNCTIONS__.config.handleSaveConfig();
}
global.__HUGO_AURA_UI_FUNCTIONS__.config.hideConfigPage();
setTimeout(() => {
const onLeaveEvent = new CustomEvent("onCurConfigPageLeave");
document.dispatchEvent(onLeaveEvent);
}, 500);
},
hideConfigPage: async () => {
const defaultHeader = document.getElementsByClassName(
"index__header__16DmR2a5"
@@ -34,6 +78,7 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
toggleSubConfig: (subPage, side) => {
if (side === global.__HUGO_AURA_UI_REACTIVES__.config.isInSubPage) return;
if (!global.__HUGO_AURA_UI_REACTIVES__.config.authenticated) return;
if (!side) {
side = !global.__HUGO_AURA_UI_REACTIVES__.config.isInSubPage;
}
@@ -49,17 +94,42 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
"aura-config-page-operation-el"
);
let pendingSubPageId = "";
let preserveOperationIdx = 0;
switch (subPage) {
case "disableLimitations":
side
? operationElArr[0].classList.add("preserve-operation")
: operationElArr[0].classList.remove("preserve-operation");
preserveOperationIdx = 0;
pendingSubPageId = "Aura.UI.Assistant.Config.DisableLimitations";
break;
case "behaviourCtrl":
preserveOperationIdx = 1;
pendingSubPageId = "Aura.UI.Assistant.Config.BehaviourCtrl";
if (!side) {
setTimeout(() => {
global.__HUGO_AURA_LOADER__[
"Aura.UI.Assistant.Config.BehaviourCtrl.PlsStatus"
].active = false;
}, 500);
}
break;
case "plugins":
// To Be Done
preserveOperationIdx = 2;
pendingSubPageId = "Aura.UI.Assistant.Config.Plugins";
break;
case "preferences":
preserveOperationIdx = 3;
pendingSubPageId = "Aura.UI.Assistant.Config.Preferences";
break;
default:
break;
}
side
? operationElArr[preserveOperationIdx].classList.add("preserve-operation")
: operationElArr[preserveOperationIdx].classList.remove(
"preserve-operation"
);
const operationAreaEl = document.getElementsByClassName(
"aura-config-page-operation-area"
)[0];
@@ -101,6 +171,150 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
global.__HUGO_AURA_UI_REACTIVES__.config.isInSubPage = side;
},
verifyAuthPassword: async () => {
const showFailedAnimation = async (el) => {
el.classList.remove("invalid");
await window.__HUGO_AURA_GLOBAL__.utils.sleep(50);
el.classList.add("invalid"); // Custom Anim
el.classList.add("is-invalid"); // Bootstrap
};
const inputEl = document.getElementById("acp-auth-user-input");
const userPasswdInput = inputEl.value;
if (!userPasswdInput || userPasswdInput.length < 8) {
showFailedAnimation(inputEl);
return false;
}
const crypto = require("crypto");
const encPasswd = crypto
.createHash("sha512")
.update(userPasswdInput + "EndlessX")
.digest("hex")
.toUpperCase();
if (
encPasswd ===
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordWithSalt
) {
await global.__HUGO_AURA_UI_FUNCTIONS__.config.hideAndResetAuthDialog();
await global.__HUGO_AURA_GLOBAL__.utils.sleep(250);
global.__HUGO_AURA_UI_REACTIVES__.config.authenticated = true;
global.__HUGO_AURA_UI_FUNCTIONS__.config.showSecondPhaseAnim();
return true;
} else {
showFailedAnimation(inputEl);
return false;
}
},
hideAndResetAuthDialog: async () => {
const acsDialogAreaEl = document.getElementsByClassName(
"aura-config-page-auth-dialog-area"
)[0];
const acpAppBarEl = document.getElementsByClassName(
"aura-config-page-header-area"
)[0];
const acpDialogTitleEl = document.getElementsByClassName(
"acp-auth-dialog-title"
)[0];
const acpDialogConfirmBtnEl = document.getElementsByClassName(
"acp-auth-confirm-btn"
)[0];
const acpDialogCancelBtnEl = document.getElementsByClassName(
"acp-auth-cancel-btn"
)[0];
const inputEl = document.getElementById("acp-auth-user-input");
acsDialogAreaEl.classList.add("acp-ada-hidden");
acpAppBarEl.classList.remove("color-reverse");
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
acsDialogAreaEl.style = "display: none;";
acpDialogTitleEl.textContent = "验证您的身份";
inputEl.value = "";
inputEl.classList.remove("invalid");
inputEl.classList.remove("is-invalid");
acpDialogConfirmBtnEl.onclick = (_evt) => {
global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword();
};
acpDialogCancelBtnEl.onclick = (_evt) => {
global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavBack();
};
},
handleACSNShow: async () => {
const acsnRootEl = document.getElementsByClassName(
"acp-config-status-notify"
)[0];
acsnRootEl.classList.remove("fully-hidden");
await global.__HUGO_AURA_GLOBAL__.utils.sleep(10);
acsnRootEl.classList.remove("hidden");
return true;
},
handleSaveConfig: async () => {
const result = global.__HUGO_AURA_CONFIG_MGR__.writeConfig(
global.__HUGO_AURA_CONFIG__
);
if (result) {
global.__HUGO_AURA_UI_REACTIVES__.config.isConfigPendingWrite = false;
const acsnRootEl = document.getElementsByClassName(
"acp-config-status-notify"
)[0];
const acsnMainContentEl = document.getElementsByClassName(
"acp-config-status-notify-main-content"
)[0];
const acsnSuccessEl = document.getElementsByClassName(
"acp-config-status-notify-success"
)[0];
const acsnAreaEl = document.getElementsByClassName(
"acp-config-status-notify-area"
)[0];
acsnMainContentEl.classList.add("acsn-main-content-hidden");
acsnAreaEl.classList.add("transparent");
await global.__HUGO_AURA_GLOBAL__.utils.sleep(250);
acsnMainContentEl.classList.add("acsn-main-content-fully-hidden");
acsnSuccessEl.classList.remove("acsn-success-fully-hidden");
await global.__HUGO_AURA_GLOBAL__.utils.sleep(50);
acsnSuccessEl.classList.remove("acsn-success-hidden");
await global.__HUGO_AURA_GLOBAL__.utils.sleep(1500);
acsnRootEl.classList.add("hidden");
await global.__HUGO_AURA_GLOBAL__.utils.sleep(500);
acsnRootEl.classList.add("fully-hidden");
await global.__HUGO_AURA_GLOBAL__.utils.sleep(10);
// Reset class
acsnMainContentEl.className = "acp-config-status-notify-main-content";
acsnAreaEl.className = "acp-config-status-notify-area";
acsnSuccessEl.className =
"acp-config-status-notify-success acsn-success-hidden acsn-success-fully-hidden";
return true;
} else {
// TODO: Error handling
}
},
initCustomUIProps: async (refresh = false) => {
const verticalHrEl = document.getElementById(
"auraConfigPageAppBarVerticalHr"
);
const spacerElArr = document.getElementsByClassName(
"aura-config-page-app-bar-spacer"
);
if (
global.__HUGO_AURA_CONFIG__.auraSettings.appearance.appBar
.actionBtnsOnRight
) {
verticalHrEl.classList.add("hidden");
spacerElArr[0].classList.remove("space-none");
spacerElArr[1].classList.add("space-none");
} else if (refresh) {
verticalHrEl.classList.remove("hidden");
spacerElArr[0].classList.add("space-none");
spacerElArr[1].classList.remove("space-none");
}
},
};
(() => {
@@ -151,26 +365,54 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
});
};
const showAnimation = async () => {
const defaultHeader = document.getElementsByClassName(
"index__header__16DmR2a5"
)[0];
global.__HUGO_AURA_UI_FUNCTIONS__.config.showSecondPhaseAnim = () => {
showOperationsAnimation();
};
const handleSettingsAuth = async () => {
const isAuthEnabled =
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordEnabled;
if (!isAuthEnabled) {
global.__HUGO_AURA_UI_REACTIVES__.config.authenticated = true;
showOperationsAnimation();
} else {
await window.__HUGO_AURA_GLOBAL__.utils.sleep(50);
const acsDialogAreaEl = document.getElementsByClassName(
"aura-config-page-auth-dialog-area"
)[0];
const acpAppBarEl = document.getElementsByClassName(
"aura-config-page-header-area"
)[0];
acsDialogAreaEl.style = "";
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
acsDialogAreaEl.classList.remove("acp-ada-hidden");
acpAppBarEl.classList.add("color-reverse");
}
};
const showAnimation = async () => {
const auraConfigPageRoot = document.getElementsByClassName(
"aura-config-page-root"
)[0];
await window.__HUGO_AURA_GLOBAL__.utils.sleep(200);
auraConfigPageRoot.className = "aura-config-page-root";
const defaultHeader = document.getElementsByClassName(
"index__header__16DmR2a5"
)[0];
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
defaultHeader.style = "display: none;";
showVersionContainerAnimation();
showHeaderAnimation();
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
showOperationsAnimation();
await handleSettingsAuth();
};
const onMounted = () => {
global.__HUGO_AURA_UI_FUNCTIONS__.config.initCustomUIProps();
applyVersionInfo();
showAnimation();

View File

@@ -0,0 +1,95 @@
.aura-config-page-auth-dialog-area {
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
background-color: rgba(255, 255, 255, 0.5);
opacity: 1;
transition: all 0.5s;
}
.acp-ada-hidden {
opacity: 0;
}
.aura-config-page-auth-dialog {
height: 40%;
width: 100%;
background-color: rgba(255, 255, 255, 0.75);
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
padding-top: 2rem;
padding-bottom: 2rem;
}
.acp-auth-dialog-title {
font-size: x-large;
margin-bottom: 1.5rem;
}
#acp-auth-user-input {
max-width: 50%;
/* background-color: rgba(255, 255, 255, 0.5); */
border-radius: 35px;
margin-bottom: 2rem;
}
#acp-auth-user-input.invalid {
animation: invalidShake 0.6s linear;
}
.acp-auth-btns-container {
display: flex;
justify-content: center;
align-items: center;
}
.acp-auth-btns-container button {
transition: all 0.25s;
}
.acp-auth-btns-container button:hover {
opacity: 0.5;
}
.acp-auth-btns-container button:active {
opacity: 0.25;
}
.acp-auth-confirm-btn,
.acp-auth-cancel-btn {
border-radius: 35px;
padding: 0.5rem;
}
.acp-auth-confirm-btn {
background: linear-gradient(135deg, #218fff 0%, #3fbaff 100%);
border: 0;
}
.acp-auth-confirm-btn .layui-icon {
font-size: 24px;
color: white;
}
.acp-auth-cancel-btn .layui-icon {
font-size: 24px;
}
.acp-auth-cancel-btn {
background-color: transparent;
margin-right: 3rem;
border: 1px solid rgba(0, 0, 0, 0.3);
}
.aura-config-page-auth-dialog-area[aura-cancel="false"] .acp-auth-cancel-btn {
display: none;
}

View File

@@ -0,0 +1,114 @@
.acp-config-status-notify {
position: absolute;
height: calc(40px + 4rem);
width: 100%;
background-color: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(5px);
filter: blur(0.1px);
z-index: 15000;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.5s;
}
.acp-config-status-notify.hidden {
backdrop-filter: blur(0.1px);
filter: unset;
opacity: 0;
}
.acp-config-status-notify.fully-hidden {
display: none;
}
.acp-config-status-notify-area {
height: 100%;
width: 100%;
background-color: rgba(255, 255, 255, 0.5);
color: rgba(0, 0, 0, 0.6);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: background-color 0.25s;
}
.acp-config-status-notify-area.transparent {
background-color: transparent;
}
.acp-config-status-notify-main-content {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
/* margin-bottom: 0.75rem; */
opacity: 1;
transition: opacity 0.25s;
}
.acp-config-status-notify-main-content.acsn-main-content-hidden {
opacity: 0;
pointer-events: none;
}
.acp-config-status-notify-main-content.acsn-main-content-fully-hidden {
display: none;
}
.acp-config-status-notify-area .acsn-main-icon {
font-size: 24px;
}
.acp-config-status-notify-area .acsn-main-title {
font-size: large;
margin-left: 0.5rem;
margin-right: 2.25rem;
}
.acp-config-status-notify-area .acsn-save-btn {
margin-top: 2px; /* 可能是中文导致的 */
opacity: 1;
transition: all 0.25s;
}
.acp-config-status-notify-area .acsn-save-btn:active {
opacity: 0.625;
}
.acp-config-status-notify-success {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
pointer-events: none;
color: white;
opacity: 1;
transition: opacity 0.25s;
}
.acp-config-status-notify-success.acsn-success-hidden {
opacity: 0;
}
.acp-config-status-notify-success.acsn-success-fully-hidden {
display: none;
}
.acp-config-status-notify-success .layui-icon {
font-size: 26px;
}
.acp-config-status-notify-success p {
font-size: large;
margin-top: 0.2rem;
}

View File

@@ -0,0 +1,33 @@
#aura-container-Aura-UI-Assistant-Config {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 1000;
}
.aura-config-page-root {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: url("../../../../../app.asar/public/ae247697b4639c92bd008d0ea7d13b53.png");
/* 这里不用 background-size: cover; 的效果反而更舒服一些... */
display: flex !important;
flex-direction: column;
align-items: center;
justify-content: center;
opacity: 1;
transform: scale(1);
transition: all 0.5s;
}
.aura-config-page-root-inactive {
opacity: 0;
transform: scale(1.5);
}

View File

@@ -0,0 +1,76 @@
.aura-config-page-header-area {
flex: 1;
display: flex;
flex-direction: row;
justify-content: flex-start;
width: 100%;
padding-left: 8px;
padding-right: 8px;
color: white;
z-index: 12000;
opacity: 1;
transform: translateY(0);
transition: all 0.5s;
}
.aura-config-page-header-area.color-reverse {
color: rgba(0, 0, 0, 0.8);
}
.aura-config-page-header-area.color-reverse
.aura-config-page-app-bar-hr-vertical {
background: rgba(0, 0, 0, 0.5);
}
.aura-config-page-header-area .iconfont {
font-size: 24px;
}
.aura-config-page-header-area .iconfont:hover {
opacity: 0.75;
cursor: pointer;
}
.aura-config-page-header-area .iconfont:active {
opacity: 0.375;
}
.aura-config-page-header-area p {
margin-top: -2px;
}
.aura-config-page-header-area.header-collapsed {
transform: translateY(-1rem);
opacity: 0;
}
.aura-config-page-app-bar {
height: 40px;
display: flex;
justify-content: flex-start;
align-items: center;
width: 100%;
}
.aura-config-page-app-bar-spacer {
flex-grow: 1;
}
.aura-config-page-app-bar-spacer.space-none {
flex-grow: 0;
}
.aura-config-page-app-bar-hr-vertical {
position: relative;
margin-left: 8px;
width: 1px;
background: rgba(255, 255, 255, 0.5);
height: 12px;
transition: background 0.5s;
}
.aura-config-page-app-bar-hr-vertical.hidden {
display: none;
}

View File

@@ -0,0 +1,155 @@
.aura-config-page-operation-area {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
flex: 1;
width: 100%;
overflow-y: auto;
}
.aura-config-page-operation-area::-webkit-scrollbar {
display: none;
}
.aura-config-page-operation-area.subpage-expanded {
flex: 15;
}
.aura-config-page-subpage-container {
width: 100%;
height: 0;
background-color: rgba(255, 255, 255, 0.825);
z-index: 6000;
overflow-y: scroll;
opacity: 0;
transition: all 0.5s;
}
.aura-config-page-subpage-container::-webkit-scrollbar {
display: none;
}
.aura-config-page-operation-area.subpage-expanded
.aura-config-page-subpage-container {
height: calc(100% - 40px - 4rem);
opacity: 1;
}
.aura-config-page-operation-container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
background-color: rgba(255, 255, 255, 0.1);
padding-left: 1rem;
padding-right: 1rem;
}
.aura-config-page-operation-container.hide-other-operations
.aura-config-page-operation-el:not(.preserve-operation) {
max-width: 0;
opacity: 0;
}
.aura-config-page-operation-container.hide-other-operations
.aura-config-page-operation-el.preserve-operation {
flex: 0.25;
}
.aura-config-page-operation-el {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex: 1;
padding-top: 2rem;
padding-bottom: 2rem;
overflow: hidden;
white-space: nowrap;
max-width: 25%;
opacity: 1;
transform: translateY(0);
transition: opacity 0.5s, transform 0.5s,
max-width cubic-bezier(0, 0.42, 0.18, 1) 0.5s;
}
.aura-config-page-operation-el.operation-el-show:hover {
cursor: pointer;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover {
cursor: not-allowed;
}
.aura-config-page-operation-el.operation-el-hidden {
transform: translateY(2rem);
opacity: 0;
}
.aura-config-page-operation-el.operation-el-show
.aura-config-page-operation-body {
opacity: 1;
transition: opacity 0.25s;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]
.aura-config-page-operation-body {
transition: opacity 0.5s;
}
.aura-config-page-operation-el.operation-el-show:not(.preserve-operation):hover
.aura-config-page-operation-body {
opacity: 0.625;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover
.aura-config-page-operation-body {
opacity: 0.25;
}
.aura-config-page-operation-el.operation-el-show:not(.preserve-operation):active
.aura-config-page-operation-body {
opacity: 0.25;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]::after {
content: "别急嘛, 还在开发呢...";
font-size: 16px;
opacity: 0;
color: white;
position: absolute;
transition: all 0.5s;
}
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover::after,
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:active::after {
opacity: 1;
}
.aura-config-page-operation-body {
display: flex;
align-items: center;
justify-content: center;
}
.aura-config-page-operation-el img {
max-width: 40px;
margin-right: 20px;
}
.aura-config-page-operation-el .config-operation-title {
color: white;
font-size: large;
}
.aura-config-page-operation-el .config-operation-description {
color: white;
opacity: 0.75;
font-size: small;
}

View File

@@ -0,0 +1,123 @@
.aura-config-page-status-container {
flex: 1;
width: 100%;
align-self: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
opacity: 1;
transition: all 0.5s;
}
.aura-config-page-status-container-hidden {
position: absolute;
opacity: 0;
}
.aura-config-page-status-main,
.aura-config-page-status-description {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.aura-config-page-status-description {
margin-top: 0.5rem;
transform: translateY(0);
opacity: 1;
transition: all 0.5s;
}
.aura-config-page-status-description.status-description-hidden {
transform: translateY(-2rem);
opacity: 0;
}
.aura-config-page-status-description p {
font-size: 18px;
margin-left: 15px;
margin-top: -2px;
color: white;
font-family: "Consolas", "Microsoft YaHei", sans-serif;
}
.aura-config-page-status-description i {
color: white;
}
.aura-config-page-central-aura-logo {
margin: 0.5rem 3rem;
width: 17.5%;
}
.aura-config-hr-vertical {
height: 3.75rem;
width: 1px;
background-color: rgba(255, 255, 255, 0.3);
margin-left: 30px;
margin-right: 30px;
border: none;
}
.aura-config-page-status-el {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: "Consolas", monospace;
color: white;
/*
.version-type {
content: "I want to use scss plz 😇"
}
*/
}
.aura-config-page-status-side {
height: 30%;
display: flex;
flex-direction: row;
align-items: center;
flex: 1;
transform: translateX(0);
opacity: 1;
transition: transform 0.5s, opacity 0.5s;
}
.aura-config-page-status-side.left-side {
justify-content: flex-end;
}
.aura-config-page-status-side.left-side.status-side-hidden {
transform: translateX(5rem);
opacity: 0;
}
.aura-config-page-status-side.right-side {
justify-content: flex-start;
}
.aura-config-page-status-side.right-side.status-side-hidden {
transform: translateX(-5rem);
opacity: 0;
}
.aura-config-page-status-el .version-type {
font-size: 20px;
font-weight: 500;
}
.aura-config-page-status-el .version-content {
font-size: 16px;
margin-top: 5px;
opacity: 0.625;
}

View File

@@ -0,0 +1,37 @@
.aura-config-page-toast-area {
z-index: 20000;
}
.aura-config-page-toast-area .toast {
--bs-toast-border-width: 0 !important;
--bs-toast-bg: #fff !important;
}
.aura-config-page-toast-area .toast-header {
background-color: rgb(255, 234, 202);
border-top-left-radius: var(--bs-toast-border-radius);
border-top-right-radius: var(--bs-toast-border-radius);
}
.aura-config-page-toast-area .toast.acp-toast-emerg .toast-header {
background-color: rgb(255, 202, 202);
}
.aura-config-page-toast-area .toast-header * {
color: rgba(234, 126, 14, 0.85);
}
.aura-config-page-toast-area .toast.acp-toast-emerg .toast-header * {
color: rgba(234, 65, 14, 0.85);
}
.aura-config-page-toast-area .toast-body p {
margin-bottom: var(--bs-toast-padding-x);
}
.aura-config-page-toast-area .toast-header .layui-icon {
font-weight: bolder;
margin-right: 0.5rem;
font-size: 18px;
}

View File

@@ -0,0 +1,8 @@
.aura-config-subpage-behaviour-control {
opacity: 1;
transition: opacity 0.5s;
}
.aura-config-subpage-behaviour-control.acs-behaviour-control-hidden {
opacity: 0;
}

View File

@@ -0,0 +1,69 @@
<div
id="acs-behaviour-control-el"
class="aura-config-subpage-behaviour-control acs-behaviour-control-hidden"
>
<ul class="nav nav-underline mb-3" role="tablist">
<li class="nav-item" role="presentation">
<button
class="nav-link active"
id="status-subpage-tab"
data-bs-toggle="pill"
data-bs-target="#status-subpage"
type="button"
role="tab"
aria-controls="status-subpage"
aria-selected="true"
>
状态
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="basic-config-tab"
data-bs-toggle="pill"
data-bs-target="#basic-config-subpage"
type="button"
role="tab"
aria-controls="basic-config-subpage"
aria-selected="false"
>
基本设置
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="security-config-tab"
data-bs-toggle="pill"
data-bs-target="#security-config-subpage"
type="button"
role="tab"
aria-controls="security-config-subpage"
aria-selected="false"
>
设备安全
</button>
</li>
</ul>
<div class="tab-content">
<div
class="tab-pane fade show active"
id="status-subpage"
role="tabpanel"
aria-labelledby="status-subpage-tab"
></div>
<div
class="tab-pane fade"
id="basic-config-subpage"
role="tabpanel"
aria-labelledby="basic-config-tab"
></div>
<div
class="tab-pane fade"
id="security-config-subpage"
role="tabpanel"
aria-labelledby="security-config-tab"
></div>
</div>
</div>

View File

@@ -0,0 +1,53 @@
(() => {
const REQUIRE_BASE =
"../../aura/ui/pages/configSubPages/behaviourCtrl/settings";
const {
settingsRenderer,
} = require(`${REQUIRE_BASE}/../../../../composables/settingsRenderer`);
const { basicSettings } = require(`${REQUIRE_BASE}/basic`);
const { deviceSecuritySettings } = require(`${REQUIRE_BASE}/deviceSecurity`);
const {
updatePlsSettingsFromLocal,
updatePlsRulesFromLocal,
} = require(`${REQUIRE_BASE}/../../../../composables/plsConfigManager`);
const initStatusPage = () => {
global.__HUGO_AURA_LOADER__[
"Aura.UI.Assistant.Config.BehaviourCtrl.PlsStatus"
].active = true;
};
const initBasicSettingsPage = () => {
const basicSubPageEl = document.getElementById("basic-config-subpage");
settingsRenderer(basicSubPageEl, basicSettings);
};
const initDeviceSecuritySettingsPage = () => {
const deviceSecuritySubPageEl = document.getElementById(
"security-config-subpage"
);
settingsRenderer(deviceSecuritySubPageEl, deviceSecuritySettings);
};
const renderSubPages = async () => {
await updatePlsSettingsFromLocal();
await updatePlsRulesFromLocal();
initBasicSettingsPage();
initDeviceSecuritySettingsPage();
};
const onMounted = () => {
const rootEl = document.getElementById("acs-behaviour-control-el");
initStatusPage();
setTimeout(() => {
rootEl.classList.remove("acs-behaviour-control-hidden");
renderSubPages(); // 如果立即渲染子页面, 此时 plsRules 还未初始化, 会导致子页面 auraIf 失效
}, 500);
};
onMounted();
})();

View File

@@ -0,0 +1,246 @@
.acs-behaviour-control-pls-status-page {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.acs-behaviour-control-pls-status-page p {
font-family: sans-serif;
}
.acs-bc-pls-status-page-pls-description {
margin-top: 0.5rem;
max-width: 80%;
opacity: 0.35;
font-size: small;
text-align: center;
}
.acs-bc-pls-status-page-main-logo {
max-width: 11%;
opacity: 0.45;
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.acs-bc-psp-operations-container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}
.acs-bc-psp-operations-container.acs-bc-psp-oper-ctnr-hidden {
display: none;
}
.acs-bc-psp-operation-btn {
--svg-color: rgb(17, 140, 255);
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-left: 0.75rem;
margin-right: 0.75rem;
transition: opacity 0.25s, color 0.5s;
}
.acs-bc-psp-operation-btn p {
font-size: normal;
margin-left: 5px;
margin-top: -1px;
color: var(--svg-color);
}
.acs-bc-psp-operation-btn svg {
max-width: 16px;
max-height: 16px;
}
.acs-bc-psp-operation-btn.acs-bc-psp-o-btn-dangerous {
--svg-color: rgb(244, 8, 8);
}
.acs-bc-psp-operation-btn[aura-disabled="true"] {
--svg-color: rgba(0, 0, 0, 0.25);
}
.acs-bc-psp-operation-btn[aura-disabled="true"]:hover,
.acs-bc-psp-operation-btn[aura-disabled="true"]:active {
cursor: not-allowed !important;
opacity: 1 !important;
}
.acs-bc-psp-operation-btn:hover,
.acs-bc-psp-operation-btn:active {
cursor: pointer;
}
.acs-bc-psp-operation-btn:hover {
opacity: 0.6;
}
.acs-bc-psp-operation-btn:active {
opacity: 0.3;
}
.acs-bc-psp-download-progress-area {
display: flex;
flex-direction: column;
width: 80%;
justify-content: center;
align-items: center;
}
.acs-bc-psp-download-progress-area.acs-bc-psp-dl-pbar-hidden {
display: none;
}
.acs-bc-psp-download-progress-area .progress {
height: 2px;
width: 80%;
margin-bottom: 0.6rem;
}
.acs-bc-psp-download-progress-area .progress-bar {
transition: all 0.15s;
}
.acs-bc-psp-download-progress-area #acsBcPspDownloadPbarDesc {
opacity: 0.6;
margin-bottom: 0.25rem;
}
.acs-bc-psp-download-progress-info-area {
display: flex;
}
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn {
margin-left: 1rem;
margin-bottom: 0.25rem;
}
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn.hidden {
display: none;
}
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn svg {
margin-top: 1px;
}
.acs-bc-pls-status-page-status-el {
display: flex;
align-items: center;
width: 40%;
padding: 0.625rem 0.25rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
}
.acs-bc-pls-status-page-status-el div {
display: flex;
align-items: center;
}
.acs-bc-pls-status-page-status-area {
flex-grow: 1;
justify-content: flex-end;
}
.acs-bc-pls-status-page-status-area-circle {
height: 10px;
width: 10px;
border-radius: 100%;
margin-right: 8px;
margin-top: 2px;
}
.acs-bc-pls-status-page-status-area.pending
.acs-bc-pls-status-page-status-area-circle {
background-color: rgba(0, 0, 0, 0.375);
}
.acs-bc-pls-status-page-status-area.pending p {
opacity: 0.5;
}
.acs-bc-pls-status-page-status-area.success
.acs-bc-pls-status-page-status-area-circle {
background-color: rgb(0, 175, 38);
}
.acs-bc-pls-status-page-status-area.success p {
color: rgb(0, 150, 33);
}
.acs-bc-pls-status-page-status-area.failed
.acs-bc-pls-status-page-status-area-circle {
background-color: rgb(175, 0, 0);
}
.acs-bc-pls-status-page-status-area.failed p {
color: rgb(175, 0, 0);
}
.acs-bc-pls-status-page-status-area.warning
.acs-bc-pls-status-page-status-area-circle {
background-color: rgb(212, 127, 0);
}
.acs-bc-pls-status-page-status-area.warning p {
color: rgb(212, 127, 0);
}
.acs-bc-pls-status-page-status-area.info
.acs-bc-pls-status-page-status-area-circle {
background-color: #3d78ff;
}
.acs-bc-pls-status-page-status-area.info p {
color: #3d78ff;
}
.acs-bc-psp-toast.body-display-none .toast-body {
display: none;
}
.acs-bc-psp-toast.body-display-none .toast-header {
border-bottom: none;
}
.acs-bc-psp-toast[closable="false"] .btn-close {
display: none;
}
.acs-bc-psp-toast[variant="success"] .toast-header {
background-color: rgb(6, 196, 65);
color: rgb(255, 255, 255);
}
.acs-bc-psp-toast[variant="warning"] .toast-header {
background-color: rgb(225, 178, 44);
color: rgb(255 255 255);
}
.acs-bc-psp-toast[variant="error"] .toast-header {
background-color: rgb(217, 57, 4);
color: rgb(255 255 255);
}
.acs-bc-psp-toast[variant="info"] .toast-header {
background-color: rgb(0, 149, 222);
color: rgb(255 255 255);
}
.acs-bc-psp-toast[variant="success"] .btn-close,
.acs-bc-psp-toast[variant="warning"] .btn-close,
.acs-bc-psp-toast[variant="error"] .btn-close,
.acs-bc-psp-toast[variant="info"] .btn-close {
color: rgb(255, 255, 255);
filter: invert(1);
opacity: 0.95;
}

View File

@@ -0,0 +1,215 @@
<div class="acs-behaviour-control-pls-status-page">
<p class="acs-bc-pls-status-page-pls-description">
HugoAura ProxyLayerServices (Aura-PLS) 是基于 Python + MITMProxy
实现的代理服务, 用于解密并修改希沃基础服务 (SeewoCore) 的 MQTT 数据包,
实现行为监控、伪造上报等功能
</p>
<img
src="../../aura/ui/static/aura_pls.png"
class="acs-bc-pls-status-page-main-logo"
/>
<div class="acs-bc-psp-operations-container">
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Refresh">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M26 18A10 10 0 1 1 16 8h6.182l-3.584 3.585L20 13l6-6l-6-6l-1.402 1.414L22.185 6H16a12 12 0 1 0 12 12Z"
/>
</svg>
<p>刷新状态</p>
</div>
<div
class="acs-bc-psp-operation-btn"
aura-disabled="true"
id="acsBcPsp-operBtn-Download"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M23.5 22H23v-2h.5a4.5 4.5 0 0 0 .36-9H23l-.1-.82a7 7 0 0 0-13.88 0L9 11h-.86a4.5 4.5 0 0 0 .36 9H9v2h-.5A6.5 6.5 0 0 1 7.2 9.14a9 9 0 0 1 17.6 0A6.5 6.5 0 0 1 23.5 22"
/>
<path
fill="var(--svg-color)"
d="M17 26.17V14h-2v12.17l-2.59-2.58L11 25l5 5l5-5l-1.41-1.41z"
/>
</svg>
<p>下载内核</p>
</div>
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Install">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="m10 6l1.414-1.414L15 8.172V0h2v8.172l3.586-3.586L22 6l-6 6z"
/>
<path
fill="var(--svg-color)"
d="M22 16a5.98 5.98 0 0 0-1.757-4.243L16 16l-4.243-4.243A6 6 0 1 0 22 16"
/>
<path
fill="var(--svg-color)"
d="M30 16a13.96 13.96 0 0 0-4.105-9.895l-1.414 1.414a12 12 0 1 1-16.962 0L6.105 6.105A13.997 13.997 0 1 0 30 16"
/>
</svg>
<p>安装服务</p>
</div>
<div
class="acs-bc-psp-operation-btn acs-bc-psp-o-btn-dangerous"
id="acsBcPsp-operBtn-Uninstall"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M30 21.4L28.6 20L25 23.6L21.4 20L20 21.4l3.6 3.6l-3.6 3.6l1.4 1.4l3.6-3.6l3.6 3.6l1.4-1.4l-3.6-3.6z"
/>
<path
fill="var(--svg-color)"
d="M15.4 30L5 23.8c-.6-.4-1-1-1-1.7V9.9c0-.7.4-1.4 1-1.7l10-5.9c.3-.2.6-.3 1-.3s.7.1 1 .3l10 5.9c.6.4 1 1 1 1.7V16h-2V9.9L16 4L6 9.9v12.2l10.5 6.2z"
/>
</svg>
<p>卸载服务</p>
</div>
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Start">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M7 28a1 1 0 0 1-1-1V5a1 1 0 0 1 1.482-.876l20 11a1 1 0 0 1 0 1.752l-20 11A1 1 0 0 1 7 28M8 6.69v18.62L24.925 16Z"
/>
</svg>
<p>启动服务</p>
</div>
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Stop">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M24 8v16H8V8zm0-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"
/>
</svg>
<p>停止服务</p>
</div>
</div>
<div class="acs-bc-psp-download-progress-area acs-bc-psp-dl-pbar-hidden">
<div class="progress" role="progressbar">
<div class="progress-bar" id="acsBcPspDownloadPbarEl"></div>
</div>
<div class="acs-bc-psp-download-progress-info-area">
<p id="acsBcPspDownloadPbarDesc">等待中...</p>
<div
class="acs-bc-psp-operation-btn hidden"
id="acsBcPspDownloadPbarCancelBtn"
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.cancelDownloadTask()"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M9 10.555L10.555 9L23 21.444L21.444 23z"
/>
<path
fill="var(--svg-color)"
d="M16 2A13.914 13.914 0 0 0 2 16a13.914 13.914 0 0 0 14 14a13.914 13.914 0 0 0 14-14A13.914 13.914 0 0 0 16 2m0 26a12 12 0 1 1 12-12a12.035 12.035 0 0 1-12 12"
/>
</svg>
<p>取消操作</p>
</div>
</div>
</div>
<div class="acs-bc-pls-status-page-status-el">
<p>安装状态</p>
<div
class="acs-bc-pls-status-page-status-area pending"
id="acs-bc-psp-installStatus-container"
>
<span class="acs-bc-pls-status-page-status-area-circle"></span>
<p id="acs-bc-psp-installStatus-text">未安装</p>
</div>
</div>
<div class="acs-bc-pls-status-page-status-el">
<p>启动状态</p>
<div
class="acs-bc-pls-status-page-status-area pending"
id="acs-bc-psp-launchStatus-container"
>
<span class="acs-bc-pls-status-page-status-area-circle"></span>
<p id="acs-bc-psp-launchStatus-text">未启动</p>
</div>
</div>
<div class="acs-bc-pls-status-page-status-el">
<p>连接状态</p>
<div
class="acs-bc-pls-status-page-status-area pending"
id="acs-bc-psp-connStatus-container"
>
<span class="acs-bc-pls-status-page-status-area-circle"></span>
<p id="acs-bc-psp-connStatus-text">已断开</p>
</div>
</div>
<div class="acs-bc-pls-status-page-status-el" style="border-bottom: none">
<p>版本</p>
<div class="acs-bc-pls-status-page-status-area">
<p id="acs-bc-psp-version-text">不可用</p>
</div>
</div>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div
id="plsStatusNotifyToast"
class="acs-bc-psp-toast toast"
data-bs-autohide="false"
>
<div class="toast-header">
<strong class="me-auto" id="plsStatusNotifyToastTitle"></strong>
<button
type="button"
class="btn-close"
data-bs-dismiss="toast"
></button>
</div>
<div class="toast-body" id="plsStatusNotifyToastBody"></div>
</div>
</div>
</div>

View File

@@ -0,0 +1,883 @@
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_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;
}
if (curPlsStats.version && curPlsStats.version !== "未知") {
const versionTextEl = document.getElementById(
"acs-bc-psp-version-text"
);
versionTextEl.textContent = "v" + curPlsStats.version;
}
},
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

@@ -0,0 +1,174 @@
const REQUIRE_BASE = ".";
const {
updatePlsConfigToRemote,
} = require(`${REQUIRE_BASE}/../../../../composables/plsConfigManager`);
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 = [
{
id: 0,
categoryName: "可访问性",
child: [
{
index: 0,
id: "plsListenPort",
type: "input",
subType: "number",
name: "PLS WS 默认监听端口",
description: "PLS 的 WebSocket 服务器将默认监听指定的端口",
reactive: true,
reactiveVal: ["root.settings"],
restart: false,
reload: false,
PLSRequired: true,
restartPLS: false,
warning: true,
warningContent: "PLS 仍会在默认端口被占用时, 自动随机端口重试",
associateVal: null,
auraIf: () => true,
defaultValue: "",
placeHolder: "输入端口号 (10000 ~ 65535)",
valueGetter: () => {
if (!global.__HUGO_AURA__.plsSettings) return "";
return global.__HUGO_AURA__.plsSettings.wsPort;
},
callbackFn: (newVal) => {
if (newVal === "" || !newVal)
return { valid: false, hint: "请输入端口号" };
const numberNewVal = Number(newVal);
if (numberNewVal === NaN || !(10000 <= numberNewVal) || !(newVal <= 65535)) {
return { valid: false, hint: "请输入合法的端口号 (10000 ~ 65535)" };
}
global.__HUGO_AURA__.plsSettings.wsPort = numberNewVal;
updatePlsConfigToRemote("wsPort", numberNewVal);
return { valid: true };
},
},
{
index: 1,
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\\", 使用 "/" 作为路径符',
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",
name: "重新生成 TLS 证书",
description: "PLS 将在下次启动时重新生成 TLS 证书",
reactive: true,
reactiveVal: ["root.settings"],
restart: false,
reload: false,
PLSRequired: true,
restartPLS: true,
associateVal: null,
auraIf: () => true,
defaultValue: false,
valueGetter: () => {
if (!global.__HUGO_AURA__.plsSettings) return "";
return global.__HUGO_AURA__.plsSettings.regenCert;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return false;
global.__HUGO_AURA__.plsSettings.regenCert = newVal;
updatePlsConfigToRemote("regenCert", newVal);
return true;
},
},
],
},
];
module.exports = { basicSettings };

View File

@@ -0,0 +1,99 @@
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

@@ -0,0 +1,104 @@
.acs-bc-dsc-fop-container {
display: flex;
}
.acs-bc-dsc-fop-please-wait,
.acs-bc-dsc-fop-on-req-error,
.acs-bc-dsc-fop-on-not-bind {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
opacity: 0.6;
}
.acs-bc-dsc-fop-please-wait svg,
.acs-bc-dsc-fop-on-req-error svg,
.acs-bc-dsc-fop-on-not-bind svg {
width: 18px;
height: 18px;
}
.acs-bc-dsc-fop-please-wait p,
.acs-bc-dsc-fop-on-req-error p,
.acs-bc-dsc-fop-on-not-bind p {
margin-left: 0.5rem;
margin-top: -1px;
}
.acs-bc-dsc-fop-please-wait[auraIf="false"],
.acs-bc-dsc-fop-on-req-error[auraIf="false"],
.acs-bc-dsc-fop-on-not-bind[auraIf="false"],
.acs-bc-dsc-fop-main[auraIf="false"] {
display: none;
}
.acs-bc-dsc-fop-main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
}
.acs-bc-dsc-fop-main .disks-container {
display: flex;
flex-direction: row;
}
.acs-bc-dsc-fop-disk-el {
padding: 5px 10px;
border-radius: 3px;
border: 0.5px solid rgba(0, 0, 0, 0.25);
margin-left: 0.375rem;
margin-right: 0.375rem;
transition: all 0.5s;
/* 没有用, 因为元素全被重新创建了 */
}
.acs-bc-dsc-fop-disk-el.active {
background-color: #1d70f2;
color: white;
border: 0.5px solid rgba(0, 0, 0, 0.125);
}
.acs-bc-dsc-fop-disk-el.active:hover {
background-color: #4e8df2;
}
.acs-bc-dsc-fop-main-hint-area {
display: flex;
flex-direction: row;
margin-top: 1rem;
margin-bottom: -0.25rem;
}
.acs-bc-dsc-fop-main-hint-area svg {
width: 18px;
height: 18px;
}
.acs-bc-dsc-fop-main-hint-area div {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.acs-bc-dsc-fop-main-hint-area p {
margin-top: -1px;
margin-left: 0.25rem;
}
.acs-bc-dsc-fop-main-hint-area .acs-bc-dsc-fop-main-hint-freeze {
margin-right: 0.5rem;
color: #1d70f2;
}
.acs-bc-dsc-fop-main-hint-area .acs-bc-dsc-fop-main-hint-unfreeze {
margin-left: 0.5rem;
}

View File

@@ -0,0 +1,91 @@
<div class="acs-bc-dsc-fop-container">
<div class="acs-bc-dsc-fop-please-wait" auraIf="true">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="currentColor"
d="M16 30a14 14 0 1 1 14-14a14 14 0 0 1-14 14m0-26a12 12 0 1 0 12 12A12 12 0 0 0 16 4"
/>
<path fill="currentColor" d="M20.59 22L15 16.41V7h2v8.58l5 5.01z" />
</svg>
<p>请稍候...</p>
</div>
<div class="acs-bc-dsc-fop-on-req-error" auraIf="false">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path fill="currentColor" d="M9 10.555L10.555 9L23 21.444L21.444 23z" />
<path
fill="currentColor"
d="M16 2A13.914 13.914 0 0 0 2 16a13.914 13.914 0 0 0 14 14a13.914 13.914 0 0 0 14-14A13.914 13.914 0 0 0 16 2m0 26a12 12 0 1 1 12-12a12.035 12.035 0 0 1-12 12"
/>
</svg>
<p>获取磁盘信息失败: <font id="acsBcDscFopOnReqErrorDetail"></font></p>
</div>
<div class="acs-bc-dsc-fop-on-not-bind" auraIf="false">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="currentColor"
d="M31.324 11.261A14.27 14.27 0 0 0 22.25 8H22v2h.25c2.608 0 5.155.837 7.246 2.372a12.18 12.18 0 0 1-7.548 7.036q.052-.605.052-1.22C22 10.366 15.635 4 7.812 4c-.929 0-1.856.09-2.757.268l-.657.13l-.13.657A14.3 14.3 0 0 0 4 7.812c0 4.124 1.78 7.831 4.598 10.426A14.2 14.2 0 0 0 8 22.254l.001.001a14.27 14.27 0 0 0 3.261 9.07l.439.53l.652-.22a14.18 14.18 0 0 0 9.237-10.046a14.18 14.18 0 0 0 10.045-9.237l.22-.652zM12.372 29.496A12.27 12.27 0 0 1 10 22.251c0-.912.113-1.796.303-2.652a14.1 14.1 0 0 0 9.105 2.349a12.18 12.18 0 0 1-7.036 7.548m7.51-9.613q-.833.116-1.694.117c-2.715 0-5.218-.904-7.247-2.412a12.4 12.4 0 0 1 4.048-5.204l-1.28-1.53A14.3 14.3 0 0 0 9.37 16.2A12.14 12.14 0 0 1 6.117 6.117A12 12 0 0 1 7.812 6C14.532 6 20 11.468 20 18.188q0 .862-.117 1.695Z"
/>
<circle cx="20" cy="2" r="2" fill="currentColor" />
<circle cx="27" cy="26" r="2" fill="currentColor" />
<circle cx="2" cy="20" r="2" fill="currentColor" />
</svg>
<p>当前设备暂未绑定学校, 无法查询冰点信息</p>
</div>
<div class="acs-bc-dsc-fop-main" auraIf="false">
<div class="disks-container">
<p class="acs-bc-dsc-fop-disk-el">C 盘</p>
</div>
<div class="acs-bc-dsc-fop-main-hint-area">
<div class="acs-bc-dsc-fop-main-hint-freeze">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="currentColor"
d="M21.415 12H28v-2h-4.585L28 5.415L26.586 4L22 8.587V4h-2v6.587L18.587 12H17V8h-2v4h-1.587L12 10.587V4h-2v4.587L5.414 4L4 5.415L8.585 10H4v2h6.585L12 13.415V15H8v2h4v1.587L10.587 20H4v2h4.587L4 26.586l1.414 1.415L10 23.415V28h2v-6.585L13.415 20H15v4h2v-4h1.585L20 21.415V28h2v-4.585l4.585 4.586L28 26.586L23.413 22H28v-2h-6.587L20 18.587V17h4v-2h-4v-1.585ZM18 18h-4v-4h4Z"
/>
</svg>
<p>已冻结</p>
</div>
<div class="acs-bc-dsc-fop-main-hint-unfreeze">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="currentColor"
d="M16 12.005a4 4 0 1 1-4 4a4.005 4.005 0 0 1 4-4m0-2a6 6 0 1 0 6 6a6 6 0 0 0-6-6M5.394 6.813L6.81 5.399l3.505 3.506L8.9 10.319zM2 15.005h5v2H2zm3.394 10.193L8.9 21.692l1.414 1.414l-3.505 3.506zM15 25.005h2v5h-2zm6.687-1.9l1.414-1.414l3.506 3.506l-1.414 1.414zm3.313-8.1h5v2h-5zm-3.313-6.101l3.506-3.506l1.414 1.414l-3.506 3.506zM15 2.005h2v5h-2z"
/>
</svg>
<p>未冻结</p>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,164 @@
// @ts-check
(() => {
const REQUIRE_BASE = "../..";
const { genRandomHex } = require(`${REQUIRE_BASE}/aura/utils/crypto`);
const composables = {
getAndUpdateDiskInfo: async (curConfig) => {
const progressingEl = document.getElementsByClassName(
"acs-bc-dsc-fop-please-wait"
)[0];
const onErrorEl = document.getElementsByClassName(
"acs-bc-dsc-fop-on-req-error"
)[0];
const onNotBindEl = document.getElementsByClassName(
"acs-bc-dsc-fop-on-not-bind"
)[0];
const mainEl = document.getElementsByClassName("acs-bc-dsc-fop-main")[0];
const diskContainerEl =
document.getElementsByClassName("disks-container")[0];
const seewoProxyPort = window._ACCEPT_DATA.data.ports.SeewoProxyHTTP;
const reqPromise = new Promise((resolve) => {
fetch(
`https://127.0.0.1:${seewoProxyPort}/forward/freeze/api/v1/get_disk_data`,
{
headers: {
accept: "application/json, text/plain, */*",
"X-Auth-Traceid": genRandomHex(),
},
}
)
.then(async (response) => {
if (response.status !== 200) {
resolve({
success: true,
data: null,
status: response.status
})
}
const parsedData = await response.json();
resolve({
success: true,
data: parsedData,
status: response.status,
});
})
.catch((e) => {
resolve({ success: false, data: null, errorObj: e });
});
});
const responseInfo = await reqPromise;
progressingEl.setAttribute("auraIf", "false");
if (!responseInfo.success) {
onNotBindEl.setAttribute("auraIf", "false");
mainEl.setAttribute("auraIf", "false");
onErrorEl.setAttribute("auraIf", "true");
const detailEl = document.getElementById("acsBcDscFopOnReqErrorDetail");
// @ts-expect-error
detailEl.textContent = responseInfo.errorObj;
return;
}
if (responseInfo.status !== 200) {
onErrorEl.setAttribute("auraIf", "false");
mainEl.setAttribute("auraIf", "false");
onNotBindEl.setAttribute("auraIf", "true");
return;
}
diskContainerEl.innerHTML = ``;
const curDisks = [];
for (const disk of responseInfo.data.data[0].disksData) {
curDisks.push({ name: disk.diskName, status: disk.protectedStatus });
}
const diskElTemplate = document.createElement("p");
diskElTemplate.classList.add("acs-bc-dsc-fop-disk-el");
if (!curConfig.enable) {
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
if (disk.status !== 0) {
// @ts-expect-error
curDiskEl.classList.add("active");
}
curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl);
}
} else {
switch (curConfig.rewriteMode) {
case "allFreeze":
{
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
// @ts-expect-error
curDiskEl.classList.add("active");
curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl);
}
}
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;
}
}
onErrorEl.setAttribute("auraIf", "false");
onNotBindEl.setAttribute("auraIf", "false");
mainEl.setAttribute("auraIf", "true");
},
};
const onMounted = () => {
const rootEl = document.getElementsByClassName(
"acs-bc-dsc-fop-container"
)[0];
const eventListener = (_event) => {
if (!global.__HUGO_AURA__.plsRules) return;
composables.getAndUpdateDiskInfo(
global.__HUGO_AURA__.plsRules.client.security.uploadFreezeInfo
);
};
rootEl.addEventListener("onAssociateValueUpdated", eventListener);
setTimeout(() => {
eventListener();
}, 100);
};
onMounted();
})();

View File

@@ -1,6 +1,5 @@
.aura-config-subpage-disable-limit-root {
opacity: 1;
transition: opacity 0.5s;
}

View File

@@ -14,7 +14,7 @@
aria-controls="auth-subpage"
aria-selected="true"
>
认证与基础设施
认证与环境
</button>
</li>
<li class="nav-item" role="presentation">
@@ -31,6 +31,20 @@
上报屏蔽
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="appearance-ctl-tab"
data-bs-toggle="pill"
data-bs-target="#appearance-ctl-subpage"
type="button"
role="tab"
aria-controls="appearance-ctl-subpage"
aria-selected="false"
>
外观与体验
</button>
</li>
</ul>
<div class="tab-content">
<div
@@ -45,5 +59,11 @@
role="tabpanel"
aria-labelledby="disable-audit-tab"
></div>
<div
class="tab-pane fade"
id="appearance-ctl-subpage"
role="tabpanel"
aria-labelledby="appearance-ctl-tab"
></div>
</div>
</div>

View File

@@ -7,6 +7,7 @@
} = require("../../aura/ui/composables/settingsRenderer");
const { authSettings } = require(`${pathBase}/auth`);
const { banAuditSettings } = require(`${pathBase}/audit`);
const { uxAndAppearanceSettings } = require(`${pathBase}/uxAppearance`);
const initAuthSubPage = () => {
const authSubPageEl = document.getElementById("auth-subpage");
@@ -18,9 +19,17 @@
settingsRenderer(banAuditSubPageEl, banAuditSettings);
};
const initUxAndAppearanceSubPage = () => {
const uxAndAppearancePageEl = document.getElementById(
"appearance-ctl-subpage"
);
settingsRenderer(uxAndAppearancePageEl, uxAndAppearanceSettings);
};
const onMounted = () => {
initAuthSubPage();
initBanAuditSubPage();
initUxAndAppearanceSubPage();
const rootEl = document.getElementById("acs-disable-limit-root-el");
setTimeout(() => {

View File

@@ -8,7 +8,8 @@ const banAuditSettings = [
id: "disableFridayReport",
type: "switch",
name: "禁用 Friday 错误统计",
description: "重置 CVTE 的 Friday 错误收集服务载入 URL, 避免意外的信息上传",
description:
"重置 CVTE 的 Friday 错误收集服务载入 URL, 避免意外的信息上传",
restart: true,
reload: false,
associateVal: null,
@@ -39,7 +40,8 @@ const banAuditSettings = [
restart: true,
reload: false,
tip: true,
tipTitle: '启用后, 可能造成部分操作出现较长延迟 (如冰点操作)。希沃管家会尝试五次上报, 均失败后才会进行下一步操作',
tipTitle:
"启用后, 可能造成部分操作出现较长延迟 (如冰点操作)。希沃管家会尝试五次上报, 均失败后才会进行下一步操作",
associateVal: null,
auraIf: () => true,
defaultValue: false,

View File

@@ -1,7 +1,7 @@
const authSettings = [
{
id: 0,
categoryName: "身份验证",
categoryName: "管家身份验证",
child: [
{
index: 0,
@@ -114,8 +114,8 @@ const authSettings = [
].enabled;
},
defaultValue: "hybrid",
templates: ["hybrid", "remoteOnly", "passwordOnly"],
templateLabels: ["混合", "仅二维码", "仅密码"],
templates: ["default", "hybrid", "remoteOnly", "passwordOnly"],
templateLabels: ["默认", "混合", "仅二维码", "仅密码"],
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.rewrite[
"vendor/passwordValidation"
@@ -131,6 +131,131 @@ const authSettings = [
},
{
id: 1,
categoryName: "屏幕锁",
child: [
{
index: 0,
id: "enableScreenLockOverride",
type: "switch",
name: "启用屏幕锁覆写功能",
description: "覆写希沃管家的屏幕锁组件, 绕过限制",
restart: false,
reload: false,
associateVal: null,
auraIf: () => true,
defaultValue: true,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.enabled;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].enabled =
newVal;
},
},
{
index: 1,
id: "disableKeyboardHook",
type: "switch",
name: "允许快捷键操作",
description: "屏蔽键盘 DLL Hook, 允许在屏幕锁中操作快捷键",
restart: false,
reload: false,
tip: true,
tipTitle: "此功能正在测试中, 可能并不稳定",
associateVal: ["rewrite.vendor/screenLock.enabled"],
auraIf: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.enabled;
},
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.disableKeyboardHook;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.rewrite[
"vendor/screenLock"
].disableKeyboardHook = newVal;
},
},
{
index: 2,
id: "screenLockAuthOverrideType",
type: "radio",
name: "覆写模式",
description: "选择一个认证覆写模式",
restart: false,
reload: false,
associateVal: ["rewrite.vendor/screenLock.enabled"],
auraIf: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.enabled;
},
defaultValue: "none",
templates: ["customActivationCode", "none"],
templateLabels: ["自定义激活码", "不修改"],
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.authRewriteType;
},
callbackFn: (newVal) => {
global.__HUGO_AURA_CONFIG__.rewrite[
"vendor/screenLock"
].authRewriteType = newVal;
},
},
{
index: 3,
id: "customActivationCode",
type: "input",
subType: "password",
name: "自定义激活码",
description: '请在屏幕锁页面下方选择 "激活码解锁" 以使用',
restart: false,
reload: false,
warning: true,
warningContent: "密码为 6 位纯数字",
associateVal: [
"rewrite.vendor/screenLock.enabled",
"rewrite.vendor/screenLock.authRewriteType",
],
auraIf: () => {
return (
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].enabled &&
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.authRewriteType === "customActivationCode"
);
},
defaultValue: "",
placeHolder: "留空表示不修改, 保留已设置值",
valueGetter: () => {
return "";
},
callbackFn: (newVal) => {
if (newVal === "" || !newVal) return { valid: true };
if (newVal.length !== 6)
return { valid: false, hint: "仅可输入 6 位密码" };
if (!/^\d+$/.test(newVal)) {
return { valid: false, hint: "仅允许纯数字密码" };
}
const __config =
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"];
const crypto = require("crypto");
const result = crypto
.createHash("md5")
.update(newVal + "auraScreenLockCrack")
.digest("hex");
__config.customActivationCode.activationCodeWithSalt = result;
return { valid: true };
},
},
],
},
{
id: 2,
categoryName: "基础设施",
child: [
{
@@ -143,6 +268,9 @@ const authSettings = [
reload: false,
tip: true,
tipTitle: "启用后, 按下 Ctrl + Shift + I 即可打开 DevTools",
warning: true,
warningContent:
"在操作不当的情况下, 有可能造成 DevTools 永久无法激活 (Electron 的 Bug), 此时请使用 Chrome 远程调试",
associateVal: null,
auraIf: () => true,
defaultValue: false,

View File

@@ -0,0 +1,62 @@
const uxAndAppearanceSettings = [
{
id: 0,
categoryName: "管家助手",
child: [
{
index: 0,
id: "autoHideEasiAssistant",
type: "switch",
name: "自动最小化管家助手",
description: "管家启动后, 自动将桌面右下角管家助手最小化至 Fab 形态",
restart: true,
reload: false,
associateVal: ["ssa.ux.easiAssistant.notDisplay"],
auraIf: () => true,
defaultValue: false,
auraDisable: () => {
if (global.__HUGO_AURA_CONFIG__.ssa.ux.easiAssistant.notDisplay) {
return { value: true, tooltip: '禁用 "隐藏管家助手" 以继续' };
} else {
return { value: false };
}
},
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.ssa.ux.easiAssistant.autoHide;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.ssa.ux.easiAssistant.autoHide = newVal;
},
},
{
index: 1,
id: "notDisplayEasiAssistant",
type: "switch",
name: "隐藏管家助手",
description: "管家启动后, 管家助手窗口将不再显示",
restart: true,
reload: false,
associateVal: ["ssa.ux.easiAssistant.autoHide"],
auraIf: () => true,
defaultValue: false,
auraDisable: () => {
if (global.__HUGO_AURA_CONFIG__.ssa.ux.easiAssistant.autoHide) {
return { value: true, tooltip: '禁用 "自动最小化管家助手" 以继续' };
} else {
return { value: false };
}
},
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.ssa.ux.easiAssistant.notDisplay;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.ssa.ux.easiAssistant.notDisplay = newVal;
},
},
],
},
];
module.exports = { uxAndAppearanceSettings };

View File

@@ -0,0 +1,8 @@
.aura-config-subpage-preferences-root {
opacity: 1;
transition: opacity 0.5s;
}
.aura-config-subpage-preferences-root.acs-preferences-root-hidden {
opacity: 0;
}

View File

@@ -0,0 +1,69 @@
<div
id="acs-preferences-root-el"
class="aura-config-subpage-preferences-root acs-preferences-root-hidden"
>
<ul class="nav nav-underline mb-3" role="tablist">
<li class="nav-item" role="presentation">
<button
class="nav-link active"
id="aura-subpage-tab"
data-bs-toggle="pill"
data-bs-target="#aura-subpage"
type="button"
role="tab"
aria-controls="aura-subpage"
aria-selected="true"
>
Aura 设置
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="debug-subpage-tab"
data-bs-toggle="pill"
data-bs-target="#debug-subpage"
type="button"
role="tab"
aria-controls="debug-subpage"
aria-selected="false"
>
调试选项
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="about-subpage-tab"
data-bs-toggle="pill"
data-bs-target="#about-subpage"
type="button"
role="tab"
aria-controls="about-subpage"
aria-selected="false"
>
关于项目
</button>
</li>
</ul>
<div class="tab-content">
<div
class="tab-pane fade show active"
id="aura-subpage"
role="tabpanel"
aria-labelledby="aura-subpage-tab"
></div>
<div
class="tab-pane fade"
id="debug-subpage"
role="tabpanel"
aria-labelledby="debug-subpage-tab"
></div>
<div
class="tab-pane fade"
id="about-subpage"
role="tabpanel"
aria-labelledby="about-subpage-tab"
></div>
</div>
</div>

View File

@@ -0,0 +1,31 @@
(() => {
const pathBase = "../../aura/ui/pages/configSubPages/preferences/settings";
const {
settingsRenderer,
} = require("../../aura/ui/composables/settingsRenderer");
const { auraSettings } = require(`${pathBase}/aura`);
const { debugSettings } = require(`${pathBase}/debug`);
const initAuraSubPage = () => {
const auraSettingsSubPageEl = document.getElementById("aura-subpage");
settingsRenderer(auraSettingsSubPageEl, auraSettings);
};
const initDebugSubPage = () => {
const debugSubPageEl = document.getElementById("debug-subpage");
settingsRenderer(debugSubPageEl, debugSettings);
};
const onMounted = () => {
initAuraSubPage();
initDebugSubPage();
const rootEl = document.getElementById("acs-preferences-root-el");
setTimeout(() => {
rootEl.classList.remove("acs-preferences-root-hidden");
}, 500);
};
onMounted();
})();

View File

@@ -0,0 +1,475 @@
// @ts-check
const functions = {
/**
*
* @param {"enc" | "update"} mode
* @param {SHA256EncryptedPassword | null} password
*/
handleEnableConfigEncryption: async (mode, password) => {
let exiPassword = "";
if (!password) {
exiPassword =
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordWithSalt;
}
switch (mode) {
case "enc":
ipcRenderer.invoke("$aura.config.setConfigEncSettings", {
target: true,
});
global.ipcRenderer.invoke("$aura.config.dispatchConfigFromRenderer", {
data: JSON.stringify(global.__HUGO_AURA_CONFIG__),
});
global.__HUGO_AURA_CONFIG_MGR__.encryptConfig(
global.__HUGO_AURA_CONFIG__,
password ? password : exiPassword
);
global.__HUGO_AURA_CONFIG_MGR__.saveEncPassword(
password ? password : exiPassword
);
break;
case "update":
const result = await global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
global.__HUGO_AURA_CONFIG__,
null
);
if (result.success) {
ipcRenderer.invoke("$aura.config.setConfigEncSettings", {
target: true,
});
global.ipcRenderer.invoke("$aura.config.dispatchConfigFromRenderer", {
data: JSON.stringify(global.__HUGO_AURA_CONFIG__),
});
global.__HUGO_AURA_CONFIG_MGR__.encryptConfig(
global.__HUGO_AURA_CONFIG__,
password ? password : exiPassword
);
global.__HUGO_AURA_CONFIG_MGR__.saveEncPassword(
password ? password : exiPassword
);
} else {
// TODO: Error handling
}
break;
}
},
/**
*
* @param {SHA256EncryptedPassword} password
*/
handle2ndPasswordPrompt: async (password) => {
const acsDialogAreaEl = document.getElementsByClassName(
"aura-config-page-auth-dialog-area"
)[0];
const acpAppBarEl = document.getElementsByClassName(
"aura-config-page-header-area"
)[0];
const acpDialogTitleEl = document.getElementsByClassName(
"acp-auth-dialog-title"
)[0];
const acpDialogConfirmBtnEl = document.getElementsByClassName(
"acp-auth-confirm-btn"
)[0];
const acpDialogCancelBtnEl = document.getElementsByClassName(
"acp-auth-cancel-btn"
)[0];
// @ts-expect-error
acsDialogAreaEl.style = "";
acpDialogTitleEl.textContent = "请再次输入密码";
await window.__HUGO_AURA_GLOBAL__.utils.sleep(50);
acpAppBarEl.classList.add("color-reverse");
acsDialogAreaEl.classList.remove("acp-ada-hidden");
const showFailedAnimation = async (el) => {
el.classList.remove("invalid");
await window.__HUGO_AURA_GLOBAL__.utils.sleep(50);
el.classList.add("invalid"); // Custom Anim
el.classList.add("is-invalid"); // Bootstrap
};
let resolveFn = null;
const awaitCompletePromise = new Promise((resolve) => {
resolveFn = resolve;
});
const handleExit = async () => {
const result = await awaitCompletePromise;
if (result) {
return { valid: true };
} else {
const inputEl = document.getElementById("auraSettingsPasswd");
// @ts-expect-error
inputEl.value = "";
return { valid: false, hint: "未能验证密码, 请重试" };
}
};
const verifyPassword = async (_clickEvt) => {
const inputEl = document.getElementById("acp-auth-user-input");
const acpDialogTitleEl = document.getElementsByClassName(
"acp-auth-dialog-title"
)[0];
// @ts-expect-error
const userPasswdInput = inputEl.value;
if (!userPasswdInput) {
showFailedAnimation(inputEl);
acpDialogTitleEl.textContent = "密码不能为空";
}
const crypto = require("crypto");
const encPasswd = crypto
.createHash("sha512")
.update(userPasswdInput + "EndlessX")
.digest("hex")
.toUpperCase();
if (encPasswd === password) {
await global.__HUGO_AURA_UI_FUNCTIONS__.config.hideAndResetAuthDialog();
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordWithSalt =
password;
if (global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig) {
functions.handleEnableConfigEncryption("update", password);
}
if (resolveFn) resolveFn(true);
return;
} else {
showFailedAnimation(inputEl);
acpDialogTitleEl.textContent = "请再试一次";
return;
}
};
// @ts-expect-error
acpDialogConfirmBtnEl.onclick = verifyPassword;
// @ts-expect-error
acpDialogCancelBtnEl.onclick = (_evt) => {
if (resolveFn) resolveFn(false);
global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavBack();
};
return await handleExit();
},
getCurAccessMethodCount: () => {
const fallbackMethods =
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.fallbackAccessMethods;
const fallbackMethodsKeys = Object.keys(fallbackMethods);
let enabledCount = 0;
for (const method of fallbackMethodsKeys) {
if (fallbackMethods[method]) {
enabledCount += 1;
}
}
if (global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.showEntryIcon) {
enabledCount += 1;
}
return enabledCount;
},
};
const auraSettings = [
{
id: 0,
categoryName: "安全性",
child: [
{
index: 0,
id: "enableAuraSettingsPasswd",
type: "switch",
name: "启用访问密码",
description: "启用后, Aura 设置 UI 需要输入密码才可访问",
restart: false,
reload: false,
associateVal: null,
auraIf: () => true,
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.auraSettings
.settingsPasswordEnabled;
},
callbackFn: async (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordEnabled =
newVal;
if (
newVal &&
global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig
) {
functions.handleEnableConfigEncryption("enc", null);
} else if (
!newVal &&
global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig
) {
await global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
global.__HUGO_AURA_CONFIG__,
null
);
}
},
},
{
index: 1,
id: "enableConfigEncryption",
type: "switch",
name: "加密配置文件",
description: "启用后, 本地配置文件将加密保存",
restart: false,
reload: false,
tip: true,
tipTitle: "配置文件将以 AES-256-GCM 加密算法在本地保存",
warning: true,
warningContent: "这可能导致性能问题",
associateVal: ["auraSettings.settingsPasswordEnabled"],
auraIf: () => {
return global.__HUGO_AURA_CONFIG__.auraSettings
.settingsPasswordEnabled;
},
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig;
},
callbackFn: async (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig = newVal;
if (newVal) {
functions.handleEnableConfigEncryption("enc", null);
} else {
await global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
global.__HUGO_AURA_CONFIG__,
null
);
}
},
},
{
index: 2,
id: "auraSettingsPasswd",
type: "input",
subType: "password",
name: "访问密码",
description: "此密码将用于访问 Aura 设置 UI",
restart: false,
reload: false,
tip: true,
tipTitle: "密码将在本地使用 SHA512 加盐存储",
associateVal: ["auraSettings.settingsPasswordEnabled"],
auraIf: () => {
return global.__HUGO_AURA_CONFIG__.auraSettings
.settingsPasswordEnabled;
},
defaultValue: "",
placeHolder: "留空表示不修改, 保留已设置值",
valueGetter: () => {
return "";
},
callbackFn: async (newVal) => {
if (newVal === "" || !newVal) return { valid: true };
if (newVal.length < 8)
return { valid: false, hint: "请输入至少 8 位密码" };
const hasNumber = /[0-9]/.test(newVal);
const hasLetter = /[a-zA-Z]/.test(newVal);
const hasSpecial = /[^a-zA-Z0-9]/.test(newVal);
const typeCount = [hasNumber, hasLetter, hasSpecial].filter(
Boolean
).length;
if (typeCount < 2) {
return {
valid: false,
hint: "请包含数字 / 字母 / 特殊字符中的至少 2 种",
};
}
const crypto = require("crypto");
const result = crypto
.createHash("sha512")
.update(newVal + "EndlessX")
.digest("hex")
.toUpperCase();
return await functions.handle2ndPasswordPrompt(result);
},
},
],
},
{
id: 1,
categoryName: "访问方式",
child: [
{
index: 0,
id: "showEntryIcon",
type: "switch",
name: "显示 HugoAura 设置图标",
description: "控制 HugoAura 设置入口图标在管家首页的显示状态",
restart: false,
reload: true,
tip: true,
tipTitle: "禁用后, HugoAura 图标将不会出现在主页右上角",
associateVal: [
"auraSettings.uiAccessMethod.fallbackAccessMethods.hotkey",
"auraSettings.uiAccessMethod.fallbackAccessMethods.touch",
],
auraIf: () => {
return true;
},
auraDisable: () => {
const fallbackMethods =
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.fallbackAccessMethods;
const fallbackMethodsKeys = Object.keys(fallbackMethods);
let anyEnabled = false;
for (const method of fallbackMethodsKeys) {
if (fallbackMethods[method]) {
anyEnabled = true;
break;
}
}
return {
value: !anyEnabled,
tooltip: !anyEnabled ? "至少启用一个备选访问方式" : "",
};
},
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.showEntryIcon;
},
callbackFn: async (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.showEntryIcon =
newVal;
},
},
{
index: 1,
id: "allowHotkeyAccess",
type: "switch",
name: "使用快捷键打开 HugoAura 设置 UI",
description:
"启用后, 在管家首页按下 Ctrl + Shift + A 以打开 HugoAura 设置",
restart: false,
reload: true,
associateVal: [
"auraSettings.uiAccessMethod.showEntryIcon",
"auraSettings.uiAccessMethod.fallbackAccessMethods.hotkey",
"auraSettings.uiAccessMethod.fallbackAccessMethods.touch",
],
auraIf: () => {
return true;
},
auraDisable: () => {
const enableCount = functions.getCurAccessMethodCount();
if (
enableCount < 2 &&
!global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.showEntryIcon &&
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.fallbackAccessMethods.hotkey
) {
return { value: true, tooltip: "无法禁用所有访问方式" };
} else {
return { value: false };
}
},
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.fallbackAccessMethods.hotkey;
},
callbackFn: async (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.fallbackAccessMethods.hotkey =
newVal;
},
},
{
index: 2,
id: "allowTouchAccess",
type: "switch",
name: "使用触摸手势打开 HugoAura 设置 UI",
description:
"启用后, 在管家首页连击 7 次右上角信息栏 ( i ) 中的版本号区域以打开 HugoAura 设置",
restart: false,
reload: true,
tip: true,
tipTitle: "请在 10 秒钟内完成连击操作, 否则计时器将被重置",
associateVal: [
"auraSettings.uiAccessMethod.showEntryIcon",
"auraSettings.uiAccessMethod.fallbackAccessMethods.hotkey",
"auraSettings.uiAccessMethod.fallbackAccessMethods.touch",
],
auraIf: () => {
return true;
},
auraDisable: () => {
const enableCount = functions.getCurAccessMethodCount();
if (
enableCount < 2 &&
!global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.showEntryIcon &&
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.fallbackAccessMethods.touch
) {
return { value: true, tooltip: "无法禁用所有访问方式" };
} else {
return { value: false };
}
},
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.fallbackAccessMethods.touch;
},
callbackFn: async (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.fallbackAccessMethods.touch =
newVal;
},
},
],
},
{
id: 2,
categoryName: "外观",
child: [
{
index: 0,
id: "actionBtnsOnRight",
type: "switch",
name: "顶栏操作类按钮右置",
description: "启用后, 顶栏的<b>返回首页按钮</b>将靠右放置",
restart: false,
reload: false,
auraIf: () => {
return true;
},
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.auraSettings.appearance.appBar
.actionBtnsOnRight;
},
callbackFn: async (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.auraSettings.appearance.appBar.actionBtnsOnRight =
newVal;
global.__HUGO_AURA_UI_FUNCTIONS__.config.initCustomUIProps(true);
},
},
],
},
];
module.exports = { auraSettings };

View File

@@ -0,0 +1,45 @@
const IPC_METHOD_BASE = "$aura.debug";
const path = require("path");
const debugSettings = [
{
id: 0,
categoryName: "日志与输出",
child: [
{
index: 0,
id: "openHugoAuraLogDir",
type: "button",
style: "outline",
name: "HugoAura 日志目录",
description: "",
restart: false,
reload: false,
associateVal: null,
auraIf: () => true,
buttonContent: "打开",
valueGetter: async () => {
if (
global.__HUGO_AURA__.auraDir &&
global.__HUGO_AURA__.auraDir !== ""
) {
return (
"目录位置: " + path.join(global.__HUGO_AURA__.auraDir, "logs")
);
} else {
return "未能获取日志目录位置";
}
},
callbackFn: async (event) => {
const childProc = require("child_process");
childProc.spawn(`explorer.exe`, [
`${path.join(global.__HUGO_AURA__.auraDir, "logs")}`,
]);
},
},
],
},
];
module.exports = { debugSettings };

4
src/aura/ui/pages/headerIcon/headerIcon.css Normal file → Executable file
View File

@@ -7,6 +7,10 @@
transition: opacity 0.25s;
}
#root.aura-header-icon-hidden .aura-header-icon {
display: none;
}
.aura-header-icon:hover {
opacity: 0.75;
}

0
src/aura/ui/pages/headerIcon/headerIcon.html Normal file → Executable file
View File

53
src/aura/ui/pages/headerIcon/headerIcon.js Normal file → Executable file
View File

@@ -1,5 +1,56 @@
global.__HUGO_AURA_UI_FUNCTIONS__.headerIcon = {
showAuraConfig: () => {
window.__HUGO_AURA_LOADER__["Aura.UI.Assistant.Config"].active = true;
if (global.__HUGO_AURA_LOADER__["Aura.UI.Assistant.Config"].active) return;
global.__HUGO_AURA_LOADER__["Aura.UI.Assistant.Config"].active = true;
},
};
(() => {
let clickCounter = 0;
let clickTimeout = null;
const onMounted = () => {
if (
!global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.showEntryIcon
) {
const rootEl = document.getElementById("root");
rootEl.classList.add("aura-header-icon-hidden");
}
if (
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.fallbackAccessMethods.hotkey
) {
document.addEventListener("keydown", (event) => {
if (event.ctrlKey && event.shiftKey && event.key === "A") {
global.__HUGO_AURA_UI_FUNCTIONS__.headerIcon.showAuraConfig();
}
});
}
if (
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.fallbackAccessMethods.touch
) {
const mesModelEl = document.getElementsByClassName(
"index__mes-modal__2hRouc6M"
)[0];
const verEl = mesModelEl.children[0];
verEl.onclick = () => {
clickCounter += 1;
if (clickCounter >= 7) {
global.__HUGO_AURA_UI_FUNCTIONS__.headerIcon.showAuraConfig();
clickCounter = 0;
if (clickTimeout) {
clearTimeout(clickTimeout);
}
}
clickTimeout = setTimeout(() => {
clickCounter = 0;
}, 10000);
};
}
};
onMounted();
})();

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Aura WebSocket KeepAlive Window</title>
<style>
:root {
opacity: 0;
}
</style>
<script>
let global = window;
</script>
<script src="../../../js/plsConnectionManager.js"></script>
</head>
<body></body>
</html>

30
src/aura/ui/pls/pushHandler.js Executable file
View File

@@ -0,0 +1,30 @@
// @ts-check
const REQUIRE_BASE = ".";
const { basicRouteHandler } = require(`${REQUIRE_BASE}/routes/basic`);
const { configRouteHandler } = require(`${REQUIRE_BASE}/routes/config`);
/**
*
* @param {PLSPush} parsedWsMsg
* @returns
*/
const pushMsgHandler = (parsedWsMsg) => {
if (!parsedWsMsg.type) return false;
const msgCategory = parsedWsMsg.type.split(".")[0];
switch (msgCategory) {
case "basic":
basicRouteHandler(parsedWsMsg);
break;
case "config":
configRouteHandler(parsedWsMsg);
break;
default:
break;
}
};
module.exports = { pushMsgHandler };

Some files were not shown because too many files have changed in this diff Show More