50 Commits

Author SHA1 Message Date
TianMiao
e9622c28ad featUpdate QQ group link in README 2026-04-29 17:35:19 +08:00
CreeperAWA
8a44af078a [ Feat] Update QQ group link in README 2026-02-16 23:36:37 +08:00
Minoricew
c5c63c6639 [🛠️ Fix] Direct unlock not show when !showTypeList.includes("direct") 2025-12-30 20:11:21 +08:00
Minoricew
420f35a5e4 [ Feat] Impl file-based IPC for Aikari installation 2025-12-16 02:04:17 +08:00
Minoricew
1031bf04bb [🔄 Chore] Add telegram links into README 2025-12-14 13:40:04 +08:00
Minoricew
db55f54cc1 [ Feat] More features related to screenLock bypass 2025-12-14 12:50:33 +08:00
Minoricew
46ca11caad [🛠️ Fix] Enc config detection issue & Add hosts file clean for Aikari uninst 2025-12-04 14:51:01 +08:00
Minorice
6ee3f99411 [🔄 Chore] Update README ((( 2025-12-04 11:47:12 +08:00
Minoricew
f2bb2ab06a [ Feat] Impl block block prompt (?) 2025-11-30 18:33:00 +08:00
Minoricew
971d3db43a [🔄 Chore] Update ISSUE_TEMPLATEs 2025-11-30 12:24:13 +08:00
Minoricew
f7feca7ff2 [ Feat] Customize usbInsertPrompt behaviour (#59) 2025-11-29 15:59:09 +08:00
Minoricew
b27b1a6573 [🛠️ Fix] Logical issues causing telemetryId cp failed 2025-11-25 00:30:45 +08:00
Minoricew
1b90932869 [🔄 Chore] Update README 2025-11-24 03:03:34 +08:00
Minoricew
c84aaef994 [ Feat] Add settings for Aikari Telemetry 2025-11-22 19:21:52 +08:00
Minoricew
8656fd0334 [🛠️ Fix] Unexpected infinite reconn for ACM 2025-11-21 02:00:30 +08:00
Minoricew
80354cc310 [🛠️ Fix] Add auto clean for prev Py-PLS installation 2025-11-18 20:12:00 +08:00
Minoricew
cd946a60fb [🛠️ Fix] Add vcredist inst check & Improve Aikari lifecycle mgmt
1. [+] 添加了对于 VC Redist 是否安装的状态检查 & 引导
2. [*] 略微调整和改进了 Aikari 的生命周期管理, 避免彻底卡死等情况, 添加更多的错误说明
2025-11-18 19:37:52 +08:00
Minorice
a6c68782a0 [🔄 Chore] Add rickroll to README (bushi 2025-11-18 02:19:14 +08:00
Minoricew
d6db3e398a [🛠️ Fix] Settings renderer 'auraDisable' bug 2025-11-18 01:00:30 +08:00
Minoricew
7afeb39275 [️ BREAKING / Feat] Bump version to v0.2.0-RC1
- [+] 完成了下载 & 安装 Aikari 的步骤
- [*] 修复了部分状态管理错误, 但没修全
2025-11-17 00:40:21 +08:00
Minoricew
b277a61923 [🔄 Chore] Prepare for Aikari (2/2) 2025-11-17 00:40:21 +08:00
Minoricew
08290301a3 [🔄 Chore] Prepare for Aikari (1/2)
1. [/] 一些无意义的重命名工作
2. [*] 修改了 WebSocket 对接逻辑
2025-11-17 00:40:21 +08:00
ntcho&ConiMite
5a29e909f2 [🔀 Merge] PR #45 into HugoAura:dev
## [ Feat] Add ASAR patch locally (main-project) (#45)

* [CI/CD] Delete the patchment of the ASAR on workflow & Change the ZIP packing dir to the whole「src」

* [🔄 Chore  / 🛠️ Fix]  Rename the workflow name & Fixed the path errors

* [🛠️ Fix]  Fixed the path error of「aura.zip」

* [🔄Chore] Separate aura.zip and core.zip

* [🛠️Fix] Fixed the path of zip files.
2025-11-09 22:12:13 +08:00
Minoricew
2b9d1c97c5 [⚠ Emg Fix] Issue #47
1. [/] 将所有 JS 中的 Magic numbers 更新到最新 SSA 的对应值, 从而解决异常
2. [?] 非常不要脸地 bump 了一个 version
2025-11-07 22:14:04 +08:00
Minoricew
abd5a10d24 [🔄 Chore] Impl #40 2025-09-02 22:50:15 +08:00
Minoricew
c857f00771 [🔄 Chore] 我还没死 (不是 2025-07-24 21:27:05 +08:00
Minorice
6b64481e2e [🔁 Chore] Update README for more project-related info
1. [+] 添加 AIGC 声明
2. [+] 添加贡献准则
3. [+] 添加鸣谢板块
2025-06-30 20:32:29 +08:00
Minoricew
fb01b27d1d [🔄 Chore] Add workflow file for HugoAura-Install JSON upd 2025-06-29 14:27:43 +08:00
Minoricew
585f1713a9 [🔁 Chore] Update SSA ver in README && Add declaration for Aikari 2025-06-23 23:50:38 +08:00
Minoricew
2ff4a28b70 [🛠️ Fix] Issue #32 & #33
1. [/] 为 Revive 元素添加了自定义事件触发, 从而正确处理 Remount 发生时的情况
2. [/] 为 Input 元素添加了正确的 onSubmit 事件处理
2025-06-22 22:26:44 +08:00
Minoricew
d5b4c4b61e [🔄 Chore] Update bugReport issue template 2025-06-21 19:52:22 +08:00
Minoricew
774d999752 [🛠 Fix] Fix issue #29 & #30
1. [/] 改进 PLS 状态同步逻辑
2. [/] 使用 classList 改变元素样式状态, 而非直接赋值 className, 从而避免 #29
2025-06-21 19:20:01 +08:00
CreeperAWA
ae2e2ff62d [🔁 Chore] update issue template to use fenced code blocks for logs 2025-06-21 18:23:00 +08:00
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
81 changed files with 5354 additions and 2393 deletions

View File

@@ -3,6 +3,19 @@ description: 反馈应用中的问题, 或预期外的行为
labels: ["Bug"]
title: "[Bug] "
body:
- type: checkboxes
attributes:
label: 在开始之前 / Before starting
options:
- label: 我了解本 Repo 仅接受 HugoAura-Main 本体 (管家 Electron 侧 Hook) 的问题反馈
required: true
- label: 我知晓, 如果这是关于 HugoAura 安装器的 Bug, 我应该前往 [HugoAura-Install Issues](https://github.com/HugoAura/HugoAura-Install/issues) 提交 Bug Report
required: true
- label: 我知晓, 如果这是 HugoAura Aikari 的 Bug, 我应该前往 [HugoAura-Aikari Issues](https://github.com/HugoAura/HugoAura-Aikari/issues) 提交 Bug Report
required: true
- label: 我知晓, 如果我将 Issue 提交到了错误的 Repo, 可能会引发开发者响应速度降低 / Issue 被忽略 / Issue 被 Close as duplicate / Issue 被 Transfer 等情况
required: true
- type: textarea
attributes:
label: 复现步骤 / Reproduce steps
@@ -48,16 +61,17 @@ body:
options:
- label: 我已经在 [HugoAura Issues](https://github.com/HugoAura/Seewo-HugoAura/issues) 中查询过相关关键词, 并确定此 Issue 是唯一的。
required: true
- label: 我已经确认, 我运行的 HugoAura 版本希沃管家客户端版本相兼容
- label: 我已经确认, 我正在使用最新版本希沃管家客户端
required: true
- type: textarea
attributes:
label: 日志 / Logs
description: 请前往 `用户文件夹/Documents/HugoAura/logs` 取得日志
description: 请前往 `偏好设置` - `调试选项` 打开日志文件夹
placeholder: |
[LOG] Logger initialized. Log file: C:\...
[LOG] [HugoAura / Loaded] Aura is loaded!
render: bash
validations:
required: false

View File

@@ -1 +1 @@
blank_issues_enabled: false
blank_issues_enabled: true

View File

@@ -3,6 +3,19 @@ description: 提交你想要的新功能
labels: ["Feature"]
title: "[Feature Request] "
body:
- type: checkboxes
attributes:
label: 在开始之前 / Before starting
options:
- label: 我了解本 Repo 仅接受 HugoAura-Main 本体 (管家 Electron 侧 Hook) 的功能请求
required: true
- label: 我知晓, 如果这是关于 HugoAura 安装器的功能请求, 我应该前往 [HugoAura-Install Issues](https://github.com/HugoAura/HugoAura-Install/issues) 提交 Feature Request
required: true
- label: 我知晓, 如果本功能请求涉及流量过滤、底层修改等目的, 我应该前往 [HugoAura-Aikari Issues](https://github.com/HugoAura/HugoAura-Aikari/issues) 提交 Feature Request
required: true
- label: 我知晓, 如果我将 Issue 提交到了错误的 Repo, 可能会引发开发者响应速度降低 / Issue 被忽略 / Issue 被 Close as duplicate / Issue 被 Transfer 等情况
required: true
- type: textarea
attributes:
label: 概述 / Overview

View File

@@ -16,5 +16,5 @@ body:
attributes:
label: 提交前请确认 / Confirm before submit
options:
- label: 请勿利用此 Issue 类型进行灌水 / 闲聊, 如果您有相关需求, 可留意后续 HugoAura 的论坛 / 用户讨论群开放情况
- label: 请勿利用此 Issue 类型进行闲聊 / 灌水, 如需闲聊, 可移步至 HugoAura 位于 STCN Forum 的论坛版块 (https://forum.smart-teach.cn/t/hugoaura)
required: true

View File

@@ -1,4 +1,4 @@
name: Patch ASAR & Pack source code
name: Pack source code & Create CI/CD Release
on:
push:
@@ -7,130 +7,81 @@ on:
branches: [dev, stable, main, master]
workflow_dispatch:
permissions: write-all
jobs:
pack:
name: Patch & Pack
name: 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
- name: Create aura.zip
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
cd ./HugoAura-Code/src/aura
zip -r ../../../Artifacts/aura.zip .
cd ../core
zip -r ../../../Artifacts/core.zip .
cd ../../..
echo "[DEBUG] Files in <Working DIR>/Artifacts directory:"
ls -la Artifacts/
- name: Upload patched ASAR
uses: actions/upload-artifact@v4
with:
name: asar-patched
path: Artifacts/app-patched.asar
- 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 aura.zip
uses: actions/upload-artifact@v4
with:
name: aura-code
path: Artifacts/aura.zip
- name: Upload core.zip
uses: actions/upload-artifact@v4
with:
name: aura-core
path: Artifacts/core.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/aura.zip
Artifacts/core.zip

View File

@@ -0,0 +1,26 @@
name: Trigger Aura Install static JSON update
on:
release:
types: [published]
jobs:
trigger-dispatch:
runs-on: ubuntu-latest
if: github.event.release.tag_name != 'vAutoBuild'
steps:
- name: Trigger repository dispatch
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.AURA_INSTALL_REPO_PAT }}
repository: HugoAura/HugoAura-Install
event-type: hugoaura_rel
client-payload: |
{
"tag_name": "${{ github.event.release.tag_name }}",
"release_name": "${{ github.event.release.name }}",
"release_url": "${{ github.event.release.html_url }}",
"repository": "${{ github.repository }}"
}

4
.gitignore vendored
View File

@@ -15,6 +15,7 @@ autoConfiguration.json
# NPM Packages
node_modules/
package-lock.json
# OS Files
desktop.ini
@@ -22,3 +23,6 @@ desktop.ini
# Editor Files
.vscode/
# Misc
*.bak

View File

@@ -3,16 +3,37 @@
<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">关于 (WIP)</a> · <a href="https://docs.aurax.cc/">文档</a> · <a href="https://github.com/HugoAura/HugoAura/issues">反馈</a> · <a href="https://forum.smart-teach.cn/t/hugoaura">社区</a>
</div>
<br />
<center>
<a href="https://forum.smart-teach.cn/d/898-xi-wo-guan-jia-zhe-kuai-zhen-de-shi-zhao-xiao-si-liao">
<img src="https://docs.aurax.cc/static/img/emg_announcement_banner.png" />
</a>
</center>
<br />
<br />
> [!TIP]
> HugoAura 的首个预览版已发布! [查看安装教程](https://github.com/HugoAura/HugoAura/wiki)
> 在[此处](https://docs.aurax.cc/)查看 HugoAura 的文档与安装教程
> [!WARNING]
>
> 我们正在进行品牌形象更新, 本仓库 (`HugoAura/Seewo-HugoAura` / HugoAura SSA Electron Injection Loader) 将会逐渐改称 `HugoAura-Main`。这是为了区分 Project HugoAura 与 HugoAura SSA Electron Injection Loader 二者。
> [!IMPORTANT]
> 已经过测试的希沃管家版本: v1.5.4.3824
> 已经过测试的希沃管家版本: v1.5.5.3917
> [!NOTE]
> **社群信息**
>
> - [QQ 群 (用户自治)](https://c.colchicum.moe)
> - [Telegram 公告频道](https://t.me/HugoAura)
> - [Telegram 群组](https://t.me/HugoAura_Chat)
> - [STCN 论坛讨论版块](https://forum.smart-teach.cn/t/hugoaura)
![Repobeats](https://repobeats.axiom.co/api/embed/69b5be5daacef624b8f5e4b8966a0b5898439a22.svg "Repobeats analytics image")
@@ -33,7 +54,7 @@
- [x] 修改希沃管家密码认证组件 (自定义密码 / 解除密码 / 重设认证方式 / ...)
- [x] 阻止希沃管家前端 Audit 上报行为
- [x] 屏蔽屏幕锁 / 自定义屏幕锁行为
- [ ] Aura 代理层服务 (篡改上报数据 / 欺骗冰冻状态)
- [x] Aura 代理层服务 (篡改上报数据 / 欺骗冰冻状态)
- [ ] 窥屏提醒
- [ ] 插件功能
@@ -45,16 +66,28 @@
> [!IMPORTANT]
> 演示图片, 请以实际安装后效果为准
<center><img src="https://s2.loli.net/2025/04/18/2KoGaXR9463tAgP.png" /></center>
<center><img src="https://s2.loli.net/2025/11/24/CUNDch9yps5IY1L.png" /></center>
<center><img src="https://s2.loli.net/2025/04/18/2lANiTpX79FcwfC.png" /></center>
<center><img src="https://s2.loli.net/2025/04/18/VNrdt7IC1PgXSpl.png" /></center>
<center><img src="https://s2.loli.net/2025/11/24/YgOqytCkxncRAs7.png" /></center>
## ⚡ 安装与使用
请参阅 [Wiki](https://github.com/HugoAura/HugoAura/wiki) 以了解安装流程。
## 🤖 AIGC 声明
![HugoAura-README_AIGC_Declaration](https://s2.loli.net/2025/06/30/MLHYONTp3E7ZbDW.png)
## 📦 贡献准则
![HugoAura-README_Community_Standards](https://s2.loli.net/2025/06/30/bFBhfYLMR45GJAd.png)
## 🎉 鸣谢
![HugoAura-README_Special_Thanks](https://s2.loli.net/2025/11/24/W9nqjxi5EcJPtLN.png)
## ❗ 免责声明
本项目仅用于研究或教育目的, 请勿将本项目用于可能违反当地法律、侵犯著作权或其他软件 EULA 的用途。若将本项目用于非法用途, 一切后果由使用者承担。开发者不承担此类行为带来的任何后果或责任。

View File

@@ -1,8 +1,7 @@
{
"name": "HugoAura",
"version": "0.1.1-pre-IV",
"version": "0.2.0-rc1-p3",
"description": "Aura for SeewoHugo",
"main": "app.asar/main.js",
"dependencies": {},
"devDependencies": {
"electron": "^36.3.2"

View File

@@ -64,27 +64,34 @@ const buildIpcMain = (electron) => {
} else {
const isWindowValid = global.__HUGO_AURA__.hookedWindows.has(windowKey);
if (!isWindowValid) {
throw new Error(
`[HugoAura / Main / IPC / ERROR] Unknown windowKey: ${windowKey}`
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");
const { applyAikariIpcHandler } = require("./ipcModules/aikariIpcHandler");
ipcMain.handle("$aura.base.restartApplication", async () => {
app.relaunch();
app.exit(0);
});
applyBaseIpcHandler(ipcMain);
applyDebugIpcHandler(ipcMain);
applyConfigIpcHandler(ipcMain);
applyFsIpcHandler(ipcMain);
applyPlsIpcHandler(ipcMain);
applyAikariIpcHandler(ipcMain);
};
module.exports = { buildIpcMain };

View File

@@ -0,0 +1,915 @@
// @ts-check
const __SCOPE = "main";
const { exec, execSync } = require("child_process");
const fs = require("fs");
const os = require("os");
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
* @param {string} logPath
* @param {string} progressFilePath
*/
handleAikariDlAndInstall: async (
channel,
callbackFn,
binPath,
logPath,
progressFilePath
) => {
let dlResult = false;
if (fs.existsSync(path.join(path.dirname(binPath), ".force"))) {
dlResult = true;
} else {
// TODO: Channel selection
const apiInfo = global.__HUGO_AURA_API__;
const getVerPromise = new Promise(async (resolveGetVerReq) => {
// ↓ 目前 channel param 没有什么用处
for (const apiDomain of apiInfo.domains) {
const reqPromise = new Promise((resolveHttpRequest) => {
nodeHttps
.get(
`${apiDomain}${apiInfo.aikariUpdate}?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:
"未能获取 Aikari 版本信息, 所有 API 域名均无法连接, 建议前往 GitHub 下载安装包并自行安装",
});
return false;
}
const aikariVersionInfo = 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(aikariVersionInfo.data.downloadUrl).includes(deviceArch)
) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: `不支持的处理器架构, 检测到的架构: "${deviceArch}"`,
});
return false;
}
const downloadFilePromise = new Promise((resolve) => {
fsComposables.downloadFile(
aikariVersionInfo.data.downloadUrl[deviceArch],
binPath,
(...args) => {
if (args[0].status === "done") {
resolve(true);
} else if (args[0].status === "failed") {
resolve(false);
}
callbackFn(...args);
}
);
});
dlResult = await downloadFilePromise;
}
if (dlResult) {
callbackFn({
id: "INSTALL_STAGE",
progress: 5,
status: "waiting",
dlUrl: null,
savePath: null,
message: "准备运行 Aikari 安装程序...",
});
const runInstPromise = new Promise((resolve) => {
exec(
`"${binPath}" /VERYSILENT /SUPPRESSMSGBOXES /LOG="${logPath}" /PROGRESS_FILE="${progressFilePath}"`,
(err, stdout, stderr) => {
if (err) {
console.error(
`[HugoAura / Main / Aikari IPC Handler / Install Aikari] Error running installer: ${stderr}`
);
resolve({ success: false });
}
resolve({ success: true });
}
);
});
if (!fs.existsSync(path.dirname(progressFilePath))) {
fs.mkdirSync(path.dirname(progressFilePath));
}
if (!fs.existsSync(progressFilePath)) {
fs.writeFileSync(progressFilePath, "");
}
callbackFn({
id: "INSTALL_STAGE",
progress: 15,
status: "progressing",
dlUrl: null,
savePath: null,
message: "正在安装 Aikari...",
});
let instFilesStageLastFakeLoadProgressPercent = -1;
const fileProgressWatcher = fs.watch(progressFilePath, (eventType) => {
if (eventType === "change") {
try {
const content = fs.readFileSync(progressFilePath, "utf-8").trim();
if (content === "") {
callbackFn({
id: "INSTALL_STAGE",
progress: 20,
status: "progressing",
dlUrl: null,
savePath: null,
message: "正在准备安装...",
});
} else {
const contentArr = content.split(";");
switch (contentArr[0]) {
case "DL_VC_REDIST":
callbackFn({
id: "INSTALL_STAGE",
progress:
20 +
Math.round((parseFloat(contentArr[1]) / 10) * 2), // 20 + [0, 20] === 20 ~ 40
status: "progressing",
dlUrl: null,
savePath: null,
message: `正在下载 VC++ 运行时... (${contentArr[1]}%)`,
});
break;
case "INST_FILES":
if (instFilesStageLastFakeLoadProgressPercent === -1) {
instFilesStageLastFakeLoadProgressPercent = 60;
} else if (instFilesStageLastFakeLoadProgressPercent < 85) {
instFilesStageLastFakeLoadProgressPercent +=
Math.random() * (5 - 1) + 1;
}
callbackFn({
id: "INSTALL_STAGE",
progress: instFilesStageLastFakeLoadProgressPercent,
status: "progressing",
dlUrl: null,
savePath: null,
message: `正在解压文件...`,
});
break;
case "INST_VC_REDIST":
callbackFn({
id: "INSTALL_STAGE",
progress: 90,
status: "progressing",
dlUrl: null,
savePath: null,
message: `正在安装 VC++ 运行时...`,
});
break;
case "DONE":
callbackFn({
id: "INSTALL_STAGE",
progress: 99,
status: "progressing",
dlUrl: null,
savePath: null,
message: `即将完成`,
});
break;
}
}
} catch (e) {
// Ignore
}
}
});
const instResult = await runInstPromise;
fileProgressWatcher.close();
callbackFn({
id: "INSTALL_STAGE",
progress: 100,
status: instResult.success ? "done" : "failed",
dlUrl: null,
savePath: null,
message: instResult.success
? "Aikari 安装成功"
: "安装失败, 请检查 %TEMP%/Aikari-Install-Temp 下的安装日志",
});
if (instResult.success) {
if (global.__HUGO_AURA__.aikariStats) {
global.__HUGO_AURA__.aikariStats.installed = true;
global.__HUGO_AURA__.aikariStats.status = "dead";
}
}
fs.unlinkSync(binPath);
fs.unlinkSync(logPath);
fs.unlinkSync(progressFilePath);
}
},
clearHostFileItem: async () => {
const AIKARI_HOST_STR =
"127.11.45.14 iot-broker-mis.seewo.com # This line is generated by HugoAura-Aikari, please do not edit or delete it";
const hostPath = path.join(
process.env.SystemRoot || "C:\\WINDOWS",
"System32",
"drivers",
"etc",
"hosts"
);
if (fs.existsSync(hostPath)) {
try {
const content = fs.readFileSync(hostPath, "utf-8");
const lines = content.split(/\r?\n/);
const newContent = lines.filter((line) => {
const shouldKeep = !line.includes(AIKARI_HOST_STR);
if (!shouldKeep) {
console.log(
`[HugoAura / IPC / Aikari] Cleaned hosts file item: ${line}`
);
}
return shouldKeep;
});
if (lines.length === newContent.length) {
return false;
}
const newData = newContent.join(os.EOL);
fs.writeFileSync(hostPath, newData, "utf-8");
return true;
} catch (err) {
console.error(
"[HugoAura / IPC / Aikari] Error cleaning hosts file: ",
err
);
return false;
}
} else {
return false;
}
},
};
/**
*
* @param {import("../../../types/main/electron").AuraIPCMain} ipcMain
*/
const applyAikariIpcHandler = (ipcMain) => {
const methodBase = "$aura.aikari";
const AIKARI_DEFAULT_INSTALL_DIR = path.join(
"C:\\Program Files",
"HugoAura",
"Aikari"
);
const AIKARI_LAUNCHER_PATH = path.join(
AIKARI_DEFAULT_INSTALL_DIR,
"Aikari-Launcher.exe"
);
const AIKARI_UNINSTALLER_PATH = path.join(
AIKARI_DEFAULT_INSTALL_DIR,
"unins000.exe"
);
const AIKARI_SVC_NAME = "HugoAuraAikari";
const AIKARI_TEMP_DL_DIR = path.join(
require("os").tmpdir(),
"Aikari-Install-Temp"
);
const AIKARI_TEMP_INSTALLER_FILENAME = path.join(
AIKARI_TEMP_DL_DIR,
"Aikari-Installer.exe"
);
const AIKARI_INSTALLER_LOG_FILENAME = path.join(
AIKARI_TEMP_DL_DIR,
"install.log"
);
const AIKARI_INSTALLER_PROGRESS_FIPC_FILENAME = path.join(
AIKARI_TEMP_DL_DIR,
"PROGRESS"
);
// Prev PLS Cfg
const OLD_PLS_INSTALL_DIR = path.join(
"C:\\Program Files",
"HugoAura PLS",
"bin"
);
const OLD_PLS_SVC_NAME = "HugoAuraPLS";
const isAikariDetached = process.argv.includes("--aikari-detach");
global.__HUGO_AURA__.aikariStats = {
installed: false,
launched: false,
detached: isAikariDetached,
connected: false,
version: "unknown",
status: "dead",
authToken: global.__HUGO_AURA_CONFIG__.aikariToken,
};
ipcMain.handle(
`${methodBase}.getIfAikariBinExists`,
/**
*
* @returns {{ success: boolean; data: { isExists: boolean }; error?: Error }}
*/
(_event, _arg) => {
try {
const result = fs.existsSync(AIKARI_LAUNCHER_PATH);
if (global.__HUGO_AURA__.aikariStats?.status === "notInstalled") {
global.__HUGO_AURA__.aikariStats.status = "dead";
}
return {
success: true,
data: { isExists: result },
};
} catch (e) {
// @ts-expect-error
global.__HUGO_AURA__.aikariStats.status = "notInstalled";
return {
success: false,
data: { isExists: false },
error: e,
};
}
}
);
ipcMain.handle(
`${methodBase}.ensureAikariInstallDir`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {any} _arg
* @returns {{ success: boolean; data: { alreadyExists: boolean; createdDir: string; }; error?: Error }}
*/
(_event, _arg) => {
const alreadyExists = fs.existsSync(AIKARI_DEFAULT_INSTALL_DIR);
if (alreadyExists) {
return {
success: true,
data: {
alreadyExists: true,
createdDir: AIKARI_DEFAULT_INSTALL_DIR,
},
};
}
try {
fs.mkdirSync(AIKARI_DEFAULT_INSTALL_DIR, { recursive: true });
return {
success: true,
data: {
alreadyExists: false,
createdDir: AIKARI_DEFAULT_INSTALL_DIR,
},
};
} catch (error) {
return {
success: false,
data: {
alreadyExists: false,
createdDir: AIKARI_DEFAULT_INSTALL_DIR,
},
error: error,
};
}
}
);
ipcMain.handle(
`${methodBase}.getAikariStatus`,
/**
*
* @returns {{ success: boolean; data: import("../../../types/shared/aikari/status").AikariStatus | null | undefined; }}
*/
(_event, _arg) => {
return {
success: true,
data: global.__HUGO_AURA__.aikariStats,
};
}
);
ipcMain.handle(
`${methodBase}.updateAikariStatus`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {import("../../../types/shared/aikari/status").AikariStatus} arg
* @returns
*/
(_event, arg) => {
global.__HUGO_AURA__.aikariStats = arg;
ipcMain.send("assistant", `${methodBase}.post.onAikariStatsUpdate`, arg);
return {
success: true,
};
}
);
ipcMain.handle(
`${methodBase}.getAikariSettings`,
/**
*
* @returns {{ success: boolean; data: Record<any, any> | null | undefined }}
*/
(_event, _arg) => {
return {
success: true,
data: global.__HUGO_AURA__.aikariSettings,
};
}
);
ipcMain.handle(
`${methodBase}.updateAikariSettings`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {Record<any, any>} arg
* @returns
*/
(_event, arg) => {
global.__HUGO_AURA__.aikariSettings = arg;
ipcMain.send(
"assistant",
`${methodBase}.post.onAikariSettingsUpdate`,
arg
);
return {
success: true,
};
}
);
ipcMain.handle(
`${methodBase}.getAikariRules`,
/**
*
* @returns {{ success: boolean; data: Record<any, any> | null | undefined }}
*/
(_event, _arg) => {
return {
success: true,
data: global.__HUGO_AURA__.aikariRules,
};
}
);
ipcMain.handle(
`${methodBase}.updateAikariRules`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {Record<any, any>} arg
* @returns
*/
(_event, arg) => {
global.__HUGO_AURA__.aikariRules = arg;
ipcMain.send("assistant", `${methodBase}.post.onAikariRulesUpdate`, arg);
return {
success: true,
};
}
);
ipcMain.on(
`${methodBase}.ws.broadcastMessageRecv`,
/**
*
* @param {import("electron").IpcMainEvent} _event
* @param {AikariResponse} arg
*/
(_event, arg) => {
ipcMain.send("assistant", `${methodBase}.ws.post.onNewMsgRecv`, arg);
}
);
ipcMain.handle(
`${methodBase}.ws.sendWsMessage`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {ClientAikariRequest} arg
*/
(_event, arg) => {
ipcMain.send(
"auraWsKeepAlive",
`${methodBase}.ws.post.onReqSendMsg`,
arg
);
}
);
ipcMain.handle(
`${methodBase}.syncAikariConfig`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} event
* @param {{ basic: Record<any, any>, rules: Record<any, any> }} arg
*/
(event, arg) => {
global.__HUGO_AURA__.aikariRules = arg.rules;
global.__HUGO_AURA__.aikariSettings = arg.basic;
ipcMain.send("*", `${methodBase}.syncAikariConfig`, arg, event.sender);
return {
success: true,
};
}
);
ipcMain.handle(
`${methodBase}.aikariLifecycleQuery`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {{ target: import("../../../types/shared/aikari/status").AikariLifecycleType }} arg
* @returns {Promise<{ success: boolean, result: boolean }>}
*/
async (_event, arg) => {
switch (arg.target) {
case "isDetached":
return { success: true, result: isAikariDetached };
case "isSvcInstalled":
return await functions.querySvcDetail(
AIKARI_SVC_NAME,
"SERVICE_NAME"
);
case "isSvcStart":
return await functions.querySvcDetail(AIKARI_SVC_NAME, "RUNNING");
default:
console.warn(
`[HugoAura / IPC / Aikari] <AikariLifecycleQuery> Invalid arg.target: ${arg.target}`
);
return {
success: false,
result: false,
};
}
}
);
ipcMain.handle(
`${methodBase}.aikariLifecycleControl`,
/**
*
* @param {*} _event
* @param {{ target: import("../../../types/shared/aikari/status").AikariLifecycleControlType }} arg
* @returns {Promise<{ success: boolean, errorObj?: Error }>}
*/
async (_event, arg) => {
const logHeader = "[HugoAura / IPC / Aikari] <AikariLifecycleControl>";
// TODO: Impl this
switch (arg.target) {
case "instSvc":
return await functions.execCommand(
logHeader,
AIKARI_LAUNCHER_PATH,
"--service install"
);
case "uninstSvc": {
const result = await functions.execCommand(
logHeader,
AIKARI_LAUNCHER_PATH,
"--service uninstall"
);
return result;
}
case "startSvc":
return await functions.execCommand(
logHeader,
"sc.exe",
`start ${AIKARI_SVC_NAME}`
);
case "stopSvc": {
const result = await functions.execCommand(
logHeader,
"sc.exe",
`stop ${AIKARI_SVC_NAME}`
);
if (result.success && global.__HUGO_AURA__.aikariStats) {
global.__HUGO_AURA__.aikariStats.connected = false;
global.__HUGO_AURA__.aikariStats.launched = false;
global.__HUGO_AURA__.aikariStats.version = "unknown";
global.__HUGO_AURA__.aikariStats.status = "dead";
ipcMain.send(
"assistant",
`${methodBase}.post.onAikariStatsUpdate`,
global.__HUGO_AURA__.aikariStats
);
ipcMain.send(
"auraWsKeepAlive",
`${methodBase}.post.aikariStopped`,
{}
);
}
return result;
}
case "inst":
// TODO: Impl Aikari INST
case "uninst":
await functions.clearHostFileItem();
return await functions.execCommand(
logHeader,
AIKARI_UNINSTALLER_PATH,
""
);
default:
return { success: false, errorObj: new Error("Method not found") };
}
}
);
ipcMain.handle(
`${methodBase}.downloadAndInstallAikari`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _evt
* @param {{channel?: "stable" | "alpha", reportTo?: import("../../../types/main/core").WindowName}} arg
* @returns {Promise<void>}
*/
async (_evt, arg) => {
if (fs.existsSync(AIKARI_TEMP_DL_DIR)) {
try {
fs.unlinkSync(AIKARI_INSTALLER_LOG_FILENAME);
fs.unlinkSync(AIKARI_INSTALLER_PROGRESS_FIPC_FILENAME);
fs.unlinkSync(AIKARI_TEMP_DL_DIR);
} catch (err) {
console.error(err);
}
} else {
fs.mkdirSync(AIKARI_TEMP_DL_DIR);
}
if (fs.existsSync(OLD_PLS_INSTALL_DIR)) {
try {
execSync(`sc stop ${OLD_PLS_SVC_NAME}`);
execSync(`sc delete ${OLD_PLS_SVC_NAME}`);
} catch (err) {
// ...
}
try {
fs.unlinkSync(OLD_PLS_INSTALL_DIR);
} catch (err) {
// ...
}
}
const channel = arg.channel ? arg.channel : "stable";
const reportWin = arg.reportTo ? arg.reportTo : "assistant";
functions.handleAikariDlAndInstall(
channel,
(status) => {
ipcMain.send(
reportWin,
`${methodBase}.post.reportAikariInstallStep`,
status
);
},
AIKARI_TEMP_INSTALLER_FILENAME,
AIKARI_INSTALLER_LOG_FILENAME,
AIKARI_INSTALLER_PROGRESS_FIPC_FILENAME
);
}
);
ipcMain.handle(
`${methodBase}.retryAikariConnect`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {any} arg
* @returns {{ success: boolean, status: "Already" | "Retrying" }}
*/
(_event, arg) => {
if (global.__HUGO_AURA__.aikariStats?.connected) {
return {
success: true,
status: "Already",
};
} else {
ipcMain.send(
"auraWsKeepAlive",
`${methodBase}.retryAikariConnect`,
arg
);
return {
success: true,
status: "Retrying",
};
}
}
);
ipcMain.handle(
`${methodBase}.forceReloadKeepAliveWin`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {any} arg
*/
(_event, arg) => {
ipcMain.send(
"auraWsKeepAlive",
`${methodBase}.post.onForceReloadRequested`,
arg
);
return { success: true };
}
);
ipcMain.handle(
`${methodBase}.post.updateRetryStatus`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {{ success: boolean; message: string }} arg
*/
(_event, arg) => {
ipcMain.send("assistant", `${methodBase}.post.updateRetryStatus`, arg);
return {
success: true,
};
}
);
};
module.exports = { applyAikariIpcHandler };

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,13 @@
// @ts-check
const __SCOPE = "main";
/**
*
* @param {import("electron").IpcMain} ipcMain
*/
const applyDebugIpcHandler = (ipcMain) => {
const methodBase = "$aura.debug";
};
module.exports = { applyDebugIpcHandler };

View File

@@ -38,10 +38,13 @@ const composableFunctions = {
progressCallback(failedTemplate);
return false;
}
if (!fs.existsSync(path.dirname(targetPath))) {
failedTemplate.message = "Path not exists";
progressCallback(failedTemplate);
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, {
@@ -49,6 +52,14 @@ const composableFunctions = {
cancelReq: null,
});
progressCallback({
id: taskId,
progress: 0,
status: "waiting",
dlUrl: url,
savePath: targetPath,
});
const fsStream = fs.createWriteStream(targetPath);
const dlReq = httpModuleIns.get(url, (response) => {
@@ -64,9 +75,12 @@ const composableFunctions = {
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, () => {});
@@ -102,6 +116,9 @@ const composableFunctions = {
fsStream.on("finish", () => {
fsStream.close();
if (hasCancelled) {
return;
}
progressCallback({
id: taskId,
progress: (100).toFixed(2),
@@ -111,9 +128,9 @@ const composableFunctions = {
dlUrl: url,
savePath: targetPath,
});
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
});
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
return true;
});
@@ -123,6 +140,10 @@ const composableFunctions = {
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;

View File

@@ -1,543 +0,0 @@
// @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__;
let plsVersionInfo = {};
const getVerPromise = new Promise((resolve) => {
// ↓ 目前 channel param 没有什么用处
nodeHttps
.get(
`${apiInfo.baseUrl}${apiInfo.plsUpdate}?channel=${channel}`,
(rep) => {
let dataChunk = "";
rep.on("data", (chunk) => {
dataChunk += chunk;
});
rep.on("end", () => {
resolve({
success: true,
data: dataChunk,
});
});
}
)
.on("error", (e) => {
resolve({
success: false,
data: null,
errorObj: e,
});
});
});
const rawResInfo = await getVerPromise;
if (!rawResInfo.success) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: "未能获取 PLS 版本信息",
errorObj: rawResInfo.errorObj ? rawResInfo.errorObj : null,
});
return false;
}
try {
plsVersionInfo = JSON.parse(rawResInfo.data);
} catch (e) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: "PLS 版本信息解析失败",
errorObj: e,
});
console.error(
"[HugoAura / IPC / PLS] Error querying PLS version info:",
e
);
return false;
}
let deviceArch = process.env.PROCESSOR_ARCHITEW6432
? process.env.PROCESSOR_ARCHITEW6432
: process.env.PROCESSOR_ARCHITECTURE;
// @ts-expect-error
deviceArch = deviceArch.toLowerCase();
if (!Object.keys(plsVersionInfo.data.downloadUrl).includes(deviceArch)) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: `处理器架构识别失败, 检测到的架构: ${deviceArch}`,
});
return false;
}
fsComposables.downloadFile(
plsVersionInfo.data.downloadUrl[deviceArch],
binPath,
callbackFn
);
},
};
/**
*
* @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);
return {
success: true,
data: { isExists: result },
};
} catch (e) {
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);
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(
"desktopAssistant",
`${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>";
if (!global.__HUGO_AURA__.plsStats?.installed) {
return { success: false, errorObj: new Error("PLS not installed") };
}
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,
});
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("desktopAssistant", `${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

@@ -14,7 +14,7 @@ class WindowHooksManager {
const hooksPath = path.join(__dirname, "../../../aura/mainProcess/hooks");
/** @type {import("../../types/main/core").HooksMap} */
/** @type {import("../../types/main/core").UIHooksMap} */
const hooks = new Map();
try {
@@ -55,6 +55,9 @@ class WindowHooksManager {
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...`

View File

@@ -168,8 +168,8 @@
observers.set(moduleKey, observer);
};
const loadResources = async (resources, type, moduleKey, isRevive) => {
if (!resources || isRevive) return [];
const loadResources = async (resources, type, moduleKey) => {
if (!resources) return [];
const resourceArray = Array.isArray(resources) ? resources : [resources];
const loadedResources = [];
@@ -232,12 +232,11 @@
const resources = new Set();
moduleResources.set(moduleKey, resources);
if (config.pageCSS) {
if (config.pageCSS && !isRevive) {
const cssResources = await loadResources(
config.pageCSS,
"css",
moduleKey,
isRevive
moduleKey
);
cssResources.forEach((resource) => resources.add(resource));
}
@@ -250,16 +249,22 @@
insertElement(target, container, config.selectorMode);
monitorParent(moduleKey, target, container, config.selectorMode);
if (config.pageScript) {
if (config.pageScript && !isRevive) {
const jsResources = await loadResources(
config.pageScript,
"js",
moduleKey,
isRevive
moduleKey
);
jsResources.forEach((resource) => resources.add(resource));
}
if (isRevive) {
const onReviveEvent = new CustomEvent(
`onLoaderElRevive:${moduleKey}`
);
document.dispatchEvent(onReviveEvent);
}
const observer = new MutationObserver(() => {
if (
!document.contains(container) &&

View File

@@ -40,6 +40,8 @@ class NetworkHook {
endOfHook: rule.endOfHook || null,
hookedContent: rule.hookedContent || null,
hookedContentFunc: rule.hookedContentFunc || null,
ruleFn: rule.ruleFn || null,
config: ruleConfig || null,
});
console.log(`[HugoAura / NetworkHook] Loaded rule: ${rulePath}`);
}
@@ -240,44 +242,49 @@ class NetworkHook {
hookContent +
endHook +
content.substring(endIdx);
const tempDir = path.join(os.tmpdir(), "hugo-aura-temp");
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
const tempFile = path.join(tempDir, path.basename(normalizedPath));
fs.writeFileSync(tempFile, content, "utf8");
return {
redirectURL: `file://${
process.platform === "win32" ? "/" : ""
}${encodeURI(tempFile.replace(/\\/g, "/"))}`, // Seewo Hugo is still on Node 12 / Electron 8 TwT
};
} else {
console.warn(
`[HugoAura / NetworkHook] Could not find match points in file: ${normalizedPath}`
);
return undefined;
return false;
}
} else if (rule.ruleFn) {
content = rule.ruleFn(content, rule.config);
} else {
console.error(
`[HugoAura / NetworkHook] Error processing rule:`,
rule
rule,
"No available hook impl found."
);
return false;
}
const tempDir = path.join(os.tmpdir(), "hugo-aura-temp");
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
const tempFile = path.join(tempDir, path.basename(normalizedPath));
fs.writeFileSync(tempFile, content, "utf8");
return {
redirectURL: `file://${
process.platform === "win32" ? "/" : ""
}${encodeURI(tempFile.replace(/\\/g, "/"))}`, // Seewo Hugo is still on Node 12 / Electron 8 TwT
};
} else {
console.error(
`[HugoAura / NetworkHook] Error processing local file: normalizedPath not exists`,
normalizedPath
);
return false;
}
} catch (err) {
console.error(
`[HugoAura / NetworkHook] Error processing local file:`,
err
);
return null;
return false;
}
}

View File

@@ -69,7 +69,7 @@ const deepMerge = (target, source) => {
class ConfigManager {
constructor() {
this.configDir = path.join(os.homedir(), "Documents", "HugoAura");
this.configDir = global.__HUGO_AURA__.auraDir;
this.configPath = path.join(this.configDir, "config.json");
this.encConfigPath = path.join(this.configDir, ".cache_2eafc8d0.dat"); // (雾
/* ↑ 不使用 .tmp 扩展名, 不然容易真被清理了 */
@@ -78,10 +78,10 @@ class ConfigManager {
this.isConfigReadFailed = false;
this.side = "unknown";
if (fs.existsSync(this.configPath)) {
this.useEncConfig = false;
} else {
if (fs.existsSync(this.encConfigPath)) {
this.useEncConfig = true;
} else {
this.useEncConfig = false;
}
if (global.__HUGO_AURA_EVENT_BUS__) {
@@ -95,6 +95,45 @@ class ConfigManager {
}
}
migrateOldConfigFile() {
if (this.configDir === path.join(os.homedir(), "Documents", "HugoAura")) {
return;
}
const oldConfigPath = path.join(
os.homedir(),
"Documents",
"HugoAura",
"config.json"
);
const oldEncConfigPath = path.join(
os.homedir(),
"Documents",
"HugoAura",
".cache_2eafc8d0.dat"
);
if (fs.existsSync(oldConfigPath)) {
console.log(
`[HugoAura / Config] Old plain config file detected at ${oldConfigPath}, migrating...`
);
fs.copyFileSync(oldConfigPath, this.configPath);
fs.unlinkSync(oldConfigPath);
this.useEncConfig = false;
} else if (fs.existsSync(oldEncConfigPath)) {
console.log(
`[HugoAura / Config] Old enc config file detected at ${oldEncConfigPath}, migrating...`
);
fs.copyFileSync(oldEncConfigPath, this.encConfigPath);
fs.unlinkSync(oldEncConfigPath);
this.useEncConfig = true;
}
console.log(
`[HugoAura / Config] Moved old config file to ${this.configDir}`
);
}
getHugoAuraConfigPath() {
return path.dirname(
this.useEncConfig ? this.encConfigPath : this.configPath
@@ -144,9 +183,14 @@ class ConfigManager {
return this.getDefaultConfig(); // should be changed, too
}
} else {
console.error("[HugoAura / Config / ERROR] Failed to decrypt config");
console.error(
"[HugoAura / Config / ERROR] Failed to decrypt config, falling back to use plain config"
);
this.useEncConfig = false;
this.isConfigReadFailed = true;
return this.getDefaultConfig(); // This behaviour should be changed later
config = JSON.parse(fs.readFileSync(this.configPath, "utf8"));
if (this.isConfigReadFailed) this.isConfigReadFailed = false;
return config; // This behaviour should be changed later
}
} else {
config = JSON.parse(fs.readFileSync(this.configPath, "utf8"));
@@ -176,7 +220,15 @@ class ConfigManager {
console.error(
"[HugoAura / Config / Write / ERROR] Failed to write config: Retrieve enc password failed"
);
return false;
console.warn(
"[HugoAura / Config / Write / WARN] Falling back to use plain config."
);
fs.writeFileSync(
this.configPath,
JSON.stringify(config, null, 2),
"utf8"
);
this.useEncConfig = false;
}
} else {
fs.writeFileSync(

View File

@@ -11,6 +11,9 @@
},
"vendor/screenLock": {
"enabled": true,
"fastfail": false,
"showDirectUnlock": false,
"clickBtnToExit": true,
"disableKeyboardHook": false,
"authRewriteType": "customActivationCode",
"customActivationCode": {
@@ -24,13 +27,39 @@
},
"disableBehaviorAudit": {
"enabled": true
},
"appearance/switchUsbInsertPromptBtn": {
"enabled": false,
"mode": "switch"
},
"appearance/banAdBlockPrompt": {
"enabled": false
}
},
"ssa": {
"ux": {
"easiAssistant": {
"autoHide": false,
"notDisplay": false
}
}
},
"auraSettings": {
"settingsPasswordEnabled": false,
"settingsPasswordWithSalt": "32703D292460CC9A3B867494D6AD9A8E4A3ADF0FAA4D6867BC4D412CC3927D02E47C6D0B1763BB53E57B2241C6193433561CDA09D7C48CA03983072B876F0965",
"encryptConfig": false,
"appearance": {}
"appearance": {
"appBar": {
"actionBtnsOnRight": false
}
},
"uiAccessMethod": {
"showEntryIcon": true,
"fallbackAccessMethods": {
"hotkey": false,
"touch": false
}
}
},
"devTools": false
}

View File

@@ -6,7 +6,12 @@ const { exec, execSync } = require("child_process");
const LOG_PREFIX = "[HugoAura / Init / Reg";
const LOG_PREFIX_FUNC = "[HugoAura / Reg";
const AURA_REGISTRY_PATH = ["HKEY_USERS", ".DEFAULT", "SOFTWARE", "HugoAura"].join("\\");
const AURA_REGISTRY_PATH = [
"HKEY_USERS",
".DEFAULT",
"SOFTWARE",
"HugoAura",
].join("\\");
class RegistryManager {
/**
@@ -341,19 +346,27 @@ class RegistryManager {
}
/**
* @param {string} relativePath
* @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(relativePath, keyName, silent = false) {
async readRegKey(
keyPath,
keyName,
silent = false,
absolute = false,
regex = null
) {
try {
const { stdout } = await new Promise((resolve, reject) => {
exec(
[
"reg",
"query",
[AURA_REGISTRY_PATH, relativePath].join("\\"),
absolute ? keyPath : [AURA_REGISTRY_PATH, keyPath].join("\\"),
"/v",
`\"${keyName}\"`,
].join(" "),
@@ -367,12 +380,12 @@ class RegistryManager {
if (!silent) {
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${relativePath}/${keyName}, stdout:`,
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${keyPath}/${keyName}, stdout:`,
stdout
);
}
const match = stdout.match(/REG_SZ\s+(.+)/);
const match = regex ? stdout.match(regex) : stdout.match(/REG_SZ\s+(.+)/);
if (!match) {
console.warn(`${LOG_PREFIX} / WARN] Data not found in stdout`);
@@ -390,10 +403,7 @@ class RegistryManager {
error: null,
};
} catch (e) {
console.error(
`${LOG_PREFIX} / ERROR] Failed to read reg key, error:`,
e
);
console.error(`${LOG_PREFIX} / ERROR] Failed to read reg key, error:`, e);
return {
success: false,
data: null,
@@ -403,18 +413,26 @@ class RegistryManager {
}
/**
* @param {string} relativePath
* @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(relativePath, keyName, silent = false) {
readRegKeySync(
keyPath,
keyName,
silent = false,
absolute = false,
regex = null
) {
try {
const readResult = execSync(
[
"reg",
"query",
[AURA_REGISTRY_PATH, relativePath].join("\\"),
absolute ? keyPath : [AURA_REGISTRY_PATH, keyPath].join("\\"),
"/v",
`\"${keyName}\"`,
].join(" "),
@@ -424,11 +442,13 @@ class RegistryManager {
if (readResult) {
if (!silent) {
console.debug(
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${relativePath}/${keyName}, stdout:`,
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${keyPath}/${keyName}, stdout:`,
readResult
);
}
const match = readResult.match(/REG_SZ\s+(.+)/);
const match = regex
? readResult.match(regex)
: readResult.match(/REG_SZ\s+(.+)/);
if (!match) {
console.warn(`${LOG_PREFIX} / WARN] Data not found in stdout`);

View File

@@ -1,6 +1,6 @@
const buildClass = (n) => {
// >>> BEGIN OF SEEWO HUGO ORIGINAL CODE <<< //
const s = n(237),
const s = n(239),
o = n(7);
class WebSocketManager {
constructor(e, t) {

View File

@@ -0,0 +1,20 @@
/// Rewrite rules basic config section begins ///
const type = "localResource";
const urlPattern = "floatWindow.js";
/// End of the rewrite rules basic config section ///
let ruleFn = (originalContent, ruleConfig) => {
if (ruleConfig.enabled) {
originalContent = `(() => { window.close() });`;
}
return originalContent;
};
module.exports = {
type,
urlPattern,
ruleFn,
};

View File

@@ -0,0 +1,31 @@
/// Rewrite rules basic config section begins ///
const type = "localResource";
const urlPattern = "usbInsertPrompt.js";
/// End of the rewrite rules basic config section ///
let ruleFn = (originalContent, ruleConfig) => {
if (ruleConfig.mode === "switch") {
originalContent = originalContent.replace(/查杀可预防设备感染,守护设备安全/g, "检测到新的设备插入");
originalContent = originalContent.replace(/开始查杀(推荐)/g, "打开 U 盘");
originalContent = originalContent.replace(
/onClick:this.handleStartVirusKilling/g,
"onClick:this.handleOpen"
);
originalContent = originalContent.replace(
/,D.a.createElement\("p",null,"打开U盘"\)/g,
""
);
} else if (ruleConfig.mode === "hide") {
originalContent = originalContent.replace(/15e3/g, "0");
}
return originalContent;
};
module.exports = {
type,
urlPattern,
ruleFn,
};

View File

@@ -46,8 +46,8 @@ const newFunction = function (e, t, b) {
_ = b(14),
M = b(44),
g = b(22),
w = (b(432), b(7)),
D = b(17),
w = (b(433), b(7)),
D = b(15),
T = b.n(D),
j = b(10),
E = b.n(j),
@@ -56,7 +56,7 @@ const newFunction = function (e, t, b) {
z = b(19),
Y = b(78),
L = b.n(Y);
b(444);
b(445);
var x,
k = 2,
S = 0,
@@ -178,7 +178,7 @@ const newFunction = function (e, t, b) {
value: s,
size: n.width || null,
})
: v.a.createElement("img", { src: b(447) })
: v.a.createElement("img", { src: b(448) })
),
d !== C &&
d !== O &&
@@ -386,8 +386,8 @@ const newFunction = function (e, t, b) {
} else {
originalFunc();
}
// ### EOR ### //
break;
// ### EOR ### //
default:
return;
}

View File

@@ -30,12 +30,12 @@ const newFunction = function (e, t, n) {
p = n.n(h),
_ = n(41),
M = n.n(_),
g = (n(808), n(7)),
g = (n(814), n(7)),
b = n(9),
y = n(8),
v = n.n(y),
w =
(n(810),
(n(816),
{
"./numberKeyboard.less": {
"ps-icon": "numberKeyboard__ps-icon__1KO_WOCz",
@@ -528,9 +528,12 @@ const newFunction = function (e, t, n) {
scanCode: "scanCode",
activationCode: "activationCode",
password: "password",
// ### BOR ### //
direct: "direct",
// ### EOR ### //
},
G = n(19);
n(812);
n(818);
function W(e) {
var t = e.canvasRender,
n = void 0 === t || t,
@@ -589,12 +592,12 @@ const newFunction = function (e, t, n) {
)
);
}
n(815);
n(821);
var J = n(64),
V = n(20);
var q,
Z,
X = n(16);
X = n(17);
function K(r) {
var a = (function () {
if ("undefined" == typeof Reflect || !Reflect.construct) return !1;
@@ -805,7 +808,7 @@ const newFunction = function (e, t, n) {
);
})(h.PureComponent))
) || Z,
oe = (n(817), n(137).a);
oe = (n(823), n(137).a);
function se(t, e) {
var n = Object.keys(t);
if (Object.getOwnPropertySymbols) {
@@ -913,6 +916,7 @@ const newFunction = function (e, t, n) {
});
}),
(o.checkPasswordCorrect = function () {
// ### BOR ### //
const originalAuthFailed = () => {
o.failCount++,
o.passwordCheckFail(),
@@ -955,6 +959,7 @@ const newFunction = function (e, t, n) {
o.state.inputPassword.join("") === o.password
? (o.props.onActivationCorrect(), (o.failCount = 0))
: customAuthFailed();
// ### EOR ### //
}),
(o.insertHtml = function () {
return p.a.createElement(
@@ -1002,7 +1007,7 @@ const newFunction = function (e, t, n) {
n
);
})(I),
me = (n(819), n(38));
me = (n(825), n(38));
function fe(r) {
var a = (function () {
if ("undefined" == typeof Reflect || !Reflect.construct) return !1;
@@ -1051,6 +1056,9 @@ const newFunction = function (e, t, n) {
f()(de, H.scanCode, "扫码"),
f()(de, H.activationCode, "激活码"),
f()(de, H.password, "密码"),
// ### BOR ### //
__config.showDirectUnlock ? f()(de, H.direct, "直接") : null,
// ### EOR ### //
de),
ge = (function (e) {
u()(i, e);
@@ -1076,6 +1084,26 @@ const newFunction = function (e, t, n) {
(r.adminHidePassword = !1),
(r.handleChooseType = function (e) {
return function () {
// ### BOR ### //
if (e === "direct" && __config.showDirectUnlock) {
global.__HUGO_AURA_BREAKUP__[
"vendor/screenLock"
].goActivationCorrect();
return;
}
if (e === "activationCode" && __config.clickBtnToExit) {
global.__HUGO_AURA_BREAKUP__["vendor/screenLock"]
.btnClickCounter++;
if (
global.__HUGO_AURA_BREAKUP__["vendor/screenLock"]
.btnClickCounter === 10
) {
global.__HUGO_AURA_BREAKUP__[
"vendor/screenLock"
].goActivationCorrect();
}
}
// ### EOR ### //
r.setState({ chooseType: e }),
(r.hasTouched = !0),
r.handleGetSelectItemPos(e);
@@ -1208,6 +1236,21 @@ const newFunction = function (e, t, n) {
{
key: "componentDidMount",
value: function () {
// ### BOR ### //
if (__config.enabled && __config.fastfail) {
this.props.onActivationCorrect();
return;
} else {
if (!global.__HUGO_AURA_BREAKUP__)
global.__HUGO_AURA_BREAKUP__ = {};
global.__HUGO_AURA_BREAKUP__["vendor/screenLock"] = {
goActivationCorrect: () => {
this.props.onActivationCorrect();
},
btnClickCounter: 0,
};
}
// ### EOR ### //
var e = this;
this.handleChangeHidePassword(),
this.chooseTypeOfIotLineStatus(function () {
@@ -1323,7 +1366,10 @@ const newFunction = function (e, t, n) {
style: { left: o, width: s },
}),
Object.keys(Me).map(function (e, t) {
return r.includes(e)
// ### BOR ### //
const showDirectCondition =
__config.showDirectUnlock && e === "direct";
return r.includes(e) || showDirectCondition
? p.a.createElement(
"div",
{
@@ -1338,6 +1384,7 @@ const newFunction = function (e, t, n) {
m
)
: null;
// ### EOR ### //
})
)
)

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ 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";
import { AikariStatus } from "./aikari/status";
type MainProcessOnlyVal<T> = T;
type RendererProcessOnlyVal<T> = T;
@@ -14,18 +14,19 @@ interface GlobalHugoAuraInfo {
fsTasks?: MainProcessOnlyVal<FSTasks>;
hookedWindows?: MainProcessOnlyVal<HookedWindowsMap>;
ipcInit?: MainProcessOnlyVal<boolean>;
plsRules?: Record<any, any> | null;
plsSettings?: Record<any, any> | null;
plsStats?: PLSStatus | null;
plsWs?: RendererProcessOnlyVal<WebSocket>;
auraDir: MainProcessOnlyVal<string>;
aikariRules?: Record<any, any> | null;
aikariSettings?: Record<any, any> | null;
aikariStats?: AikariStatus | null;
aikariWs?: RendererProcessOnlyVal<WebSocket>;
uiHooks?: MainProcessOnlyVal<UIHooksMap>;
windowHooks?: MainProcessOnlyVal<WindowHooksMap>;
version: RendererProcessOnlyVal<string>;
}
interface GlobalHugoAuraApiInfo {
baseUrl: string;
plsUpdate: string;
domains: string[];
aikariUpdate: string;
auraUpdate: string;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,12 +18,12 @@ const showRelaunchToast = () => {
if (!toastBs.isShown()) toastBs.show();
};
const showRelaunchPLSToast = () => {
const toast = document.getElementById("relaunchPlsNotifyToast");
const showRelaunchAikariToast = () => {
const toast = document.getElementById("relaunchAikariNotifyToast");
const toastBs = bootstrap.Toast.getOrCreateInstance(toast);
if (global.__HUGO_AURA__.plsStats.detached) {
const relaunchBtn = document.getElementById("plsRelaunchBtn");
if (global.__HUGO_AURA__.aikariStats.detached) {
const relaunchBtn = document.getElementById("aikariRelaunchBtn");
relaunchBtn.disabled = true;
relaunchBtn.textContent = "分离模式下无法执行";
}
@@ -37,12 +37,28 @@ const showToast = (entry) => {
} 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.removeAttribute("data-bs-toggle");
el.removeAttribute("data-bs-placement");
el.removeAttribute("data-bs-title");
el.classList.remove("ase-operation-area-disabled");
const tooltipIns = bootstrap.Tooltip.getInstance(el);
if (tooltipIns) {
tooltipIns.dispose();
}
}
*/
};
const insertOrRemoveEl = (parent, child, isInsert = true) => {
@@ -71,7 +87,12 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
switchEl.checked = elValue;
switchEl.addEventListener("change", async (event) => {
showToast(entry);
await entry.callbackFn(event.target.checked);
await entry.callbackFn(
event.target.checked,
switchEl,
operationArea,
descriptionArea
);
});
operationArea.classList.add("form-check", "form-switch");
return switchEl;
@@ -79,7 +100,18 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
case "radio": {
const elValue = entry.valueGetter();
const elArr = [];
for (const template of entry.templates) {
let targetTemplatesArr = [];
let targetTemplateLablesArr = [];
targetTemplatesArr =
typeof entry.templates === "function"
? entry.templates()
: entry.templates;
targetTemplateLablesArr =
typeof entry.templateLabels === "function"
? entry.templateLabels()
: entry.templateLabels;
for (const template of targetTemplatesArr) {
const inlineContainerEl = document.createElement("div");
inlineContainerEl.classList.add("form-check", "form-check-inline");
const radioEl = document.createElement("input");
@@ -87,12 +119,17 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
radioEl.classList.add("form-check-input");
radioEl.type = "radio";
radioEl.name = `${entry.id}Radios`;
radioEl.id = `${entry.id}Radio${entry.templates.indexOf(template)}`;
radioEl.id = `${entry.id}Radio${targetTemplatesArr.indexOf(template)}`;
radioEl.checked = template === elValue ? true : false;
radioEl.addEventListener("change", async (event) => {
if (event.target.checked) {
showToast(entry);
await entry.callbackFn(event.target.value);
await entry.callbackFn(
event.target.value,
radioEl,
operationArea,
descriptionArea
);
}
});
inlineContainerEl.appendChild(radioEl);
@@ -100,7 +137,53 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
labelEl.classList.add("form-check-label");
labelEl.setAttribute("for", radioEl.id);
labelEl.textContent =
entry.templateLabels[entry.templates.indexOf(template)];
targetTemplateLablesArr[targetTemplatesArr.indexOf(template)];
inlineContainerEl.appendChild(labelEl);
elArr.push(inlineContainerEl);
}
return elArr;
}
case "checkbox": {
const elValue = entry.valueGetter();
const elArr = [];
let targetTemplatesArr = [];
let targetTemplateLablesArr = [];
targetTemplatesArr =
typeof entry.templates === "function"
? entry.templates()
: entry.templates;
targetTemplateLablesArr =
typeof entry.templateLabels === "function"
? entry.templateLabels()
: entry.templateLabels;
for (const templateName of targetTemplatesArr) {
const inlineContainerEl = document.createElement("div");
inlineContainerEl.classList.add("form-check", "form-check-inline");
const chkBoxEl = document.createElement("input");
chkBoxEl.value = templateName;
chkBoxEl.classList.add("form-check-input");
chkBoxEl.type = "checkbox";
chkBoxEl.name = `${entry.id}Checkbox`;
chkBoxEl.id = `${entry.id}Checkbox${targetTemplatesArr.indexOf(
templateName
)}`;
chkBoxEl.checked = elValue.includes(templateName) ? true : false;
chkBoxEl.addEventListener("change", async (event) => {
showToast(entry);
await entry.callbackFn(
event.target.value,
chkBoxEl,
operationArea,
descriptionArea
);
});
inlineContainerEl.appendChild(chkBoxEl);
const labelEl = document.createElement("label");
labelEl.classList.add("form-check-label");
labelEl.setAttribute("for", chkBoxEl.id);
labelEl.textContent =
targetTemplateLablesArr[targetTemplatesArr.indexOf(templateName)];
inlineContainerEl.appendChild(labelEl);
elArr.push(inlineContainerEl);
}
@@ -113,8 +196,14 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
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);
const handleSubmit = async (event = null) => {
const result = await entry.callbackFn(
event ? event.target.value : inputEl.value,
inputEl,
operationArea,
descriptionArea
);
const success = result.valid;
if (success) {
showToast(entry);
@@ -129,10 +218,37 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
descriptionArea.textContent = result.hint;
descriptionArea.classList.add("ase-desc-error-hint");
}
};
inputEl.addEventListener("change", async (event) => {
await handleSubmit(event);
});
inputEl.onsubmit = async (evt) => {
evt.preventDefault();
await handleSubmit();
};
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;
}
@@ -160,29 +276,29 @@ const renderNormalSettingsItem = (entry, formEl) => {
powerIcon.setAttribute("data-bs-title", "需要重启 Electron 进程");
entryTitle.appendChild(powerIcon);
}
if (entry.PLSRequired) {
const plsIcon = document.createElement("i");
plsIcon.classList.add(
if (entry.aikariRequired) {
const aikariIcon = document.createElement("i");
aikariIcon.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);
aikariIcon.setAttribute("data-bs-toggle", "tooltip");
aikariIcon.setAttribute("data-bs-placement", "top");
aikariIcon.setAttribute("data-bs-title", "需要 Aikari 支持");
entryTitle.appendChild(aikariIcon);
}
if (entry.restartPLS) {
const plsIcon = document.createElement("i");
plsIcon.classList.add(
if (entry.restartAikari) {
const aikariIcon = document.createElement("i");
aikariIcon.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);
aikariIcon.setAttribute("data-bs-toggle", "tooltip");
aikariIcon.setAttribute("data-bs-placement", "top");
aikariIcon.setAttribute("data-bs-title", "需要重启 Aikari 进程");
entryTitle.appendChild(aikariIcon);
}
if (entry.reload) {
const reloadIcon = document.createElement("i");
@@ -243,43 +359,46 @@ const renderNormalSettingsItem = (entry, formEl) => {
insertOrRemoveEl(entryOperationArea, targetEl, true);
}
};
const channel = entry.PLSRequired
? "onPLSConfigUpdate"
const channel = entry.aikariRequired
? "onAikariConfigUpdate"
: "onHugoAuraConfigUpdate";
entryContainerEl.addEventListener(channel, evtListener);
// createOnLeaveEvtListener(channel, evtListener);
}
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);
}
} else {
el.setAttribute("data-bs-toggle", "");
el.setAttribute("data-bs-placement", "");
el.setAttribute("data-bs-title", "");
el.classList.remove("ase-operation-area-disabled");
}
const updateDisableStatus = (entry) => {
const isDisabledRet = entry.auraDisable();
setDisableStatus(
entryOperationArea,
isDisabledRet.value,
isDisabledRet.tooltip
);
};
if (entry.PLSRequired) {
if (!global.__HUGO_AURA__.plsStats.connected) {
setDisableStatus(entryOperationArea, true, "连接至 PLS 以继续");
if (entry.auraDisable) {
updateDisableStatus(entry);
}
if (entry.aikariRequired) {
if (!global.__HUGO_AURA__.aikariStats.connected && !entry.alwaysEnable) {
setDisableStatus(entryOperationArea, true, "连接至 Aikari 以继续");
}
const evtListener = (event) => {
if (event.detail.connected) {
setDisableStatus(entryOperationArea, false);
try {
updateDisableStatus(entry);
} catch {
setDisableStatus(entryOperationArea, false);
}
} else {
setDisableStatus(entryOperationArea, true, "连接至 PLS 以继续");
if (!entry.alwaysEnable) {
setDisableStatus(entryOperationArea, true, "连接至 Aikari 以继续");
}
}
};
entryContainerEl.addEventListener("onPLSStatsUpdate", evtListener);
// createOnLeaveEvtListener("onPLSStatsUpdate", evtListener);
entryContainerEl.addEventListener("onAikariStatsUpdate", evtListener);
// createOnLeaveEvtListener("onAikariStatsUpdate", evtListener);
}
entryContainerEl.appendChild(entryOperationArea);
const isShow = entry.auraIf();
@@ -293,9 +412,13 @@ const renderNormalSettingsItem = (entry, formEl) => {
isShow
? cls.remove("aura-settings-entry-hidden")
: cls.add("aura-settings-entry-hidden");
if (entry.auraDisable && !entry.alwaysEnable) {
updateDisableStatus(entry);
}
};
const channel = entry.PLSRequired
? "onPLSConfigUpdate"
const channel = entry.aikariRequired
? "onAikariConfigUpdate"
: "onHugoAuraConfigUpdate";
entryContainerEl.addEventListener(channel, evtListener);
// createOnLeaveEvtListener(channel, evtListener);
@@ -337,7 +460,9 @@ const renderPreviewItem = (entry, formEl) => {
};
document.addEventListener(
eventChannel === "pls" ? "onPLSConfigUpdate" : "onHugoAuraConfigUpdate",
eventChannel === "aikari"
? "onAikariConfigUpdate"
: "onHugoAuraConfigUpdate",
eventListener
);
createOnLeaveEvtListener(eventListener); // Clean up
@@ -378,7 +503,9 @@ const settingsRenderer = (pendingEl, settingsObj) => {
}
pendingEl.appendChild(formEl);
global.__HUGO_AURA_GLOBAL__.utils.refreshBsTooltip();
global.__HUGO_AURA_GLOBAL__.utils.refreshBsTooltip(
".aura-settings-entry-property-icon"
);
};
module.exports = { settingsRenderer };

View File

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

2
src/aura/ui/hookDefinitions/desktopAssistant.js Executable file → Normal file
View File

@@ -6,7 +6,7 @@
const def = {
targets: {},
globalStyles: ["ui/css/global.css"],
globalJS: ["ui/js/global.js", "ui/js/plsConnectionManager.js"],
globalJS: ["ui/js/global.js", "ui/js/pageGlobal/desktopAssistant.js"],
onLoaded: `
console.log('[HugoAura / UI / Hooks / Desktop Assistant] Page loaded.');
`,

View File

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

View File

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

View File

@@ -4,16 +4,16 @@
/* Util: BootStrap Tooltip Ctrl */
let tooltipTriggerCache = null;
const refreshBsTooltip = () => {
const refreshBsTooltip = (selector = '[data-bs-toggle="tooltip"]') => {
if (tooltipTriggerCache) {
[...tooltipTriggerCache].map((el) =>
bootstrap.Tooltip.getInstance(el).disable()
);
[...tooltipTriggerCache].map((el) => {
if (bootstrap.Tooltip.getInstance(el)) {
bootstrap.Tooltip.getInstance(el).disable();
}
});
}
const tooltipTriggerList = document.querySelectorAll(
'[data-bs-toggle="tooltip"]'
);
const tooltipTriggerList = document.querySelectorAll(selector);
tooltipTriggerCache = tooltipTriggerList;
[...tooltipTriggerList].map(
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)

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

@@ -1,330 +0,0 @@
// @ts-check
(() => {
if (!global.__HUGO_AURA__.plsStats)
global.__HUGO_AURA__.plsStats = {
installed: false,
detached: false,
connected: false,
launched: false,
status: "unknown",
version: "未知",
authToken: "",
};
const IPC_METHOD_BASE = "$aura.pls";
const REQUIRE_BASE = "../../aura/ui";
const __SCOPE = "desktopAssistant";
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 trustToken = window._ACCEPT_DATA.getData("deviceId");
const conjToken = authToken + "AuraXAuth" + trustToken + "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) {
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").global.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();
}
);
};
onSetup();
})();

View File

@@ -7,11 +7,57 @@
<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>
@@ -99,8 +145,8 @@
<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>
@@ -112,7 +158,7 @@
<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>
@@ -247,14 +293,14 @@
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div
id="relaunchPlsNotifyToast"
id="relaunchAikariNotifyToast"
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>
<strong class="me-auto">重启 Aikari 进程以应用设置</strong>
<button
type="button"
class="btn-close"
@@ -262,13 +308,13 @@
></button>
</div>
<div class="toast-body">
<p>请重启 PLS 进程以应用修改的设置</p>
<p>请重启 Aikari 进程以应用修改的设置</p>
<p>已修改的配置将自动保存</p>
<button
type="button"
class="btn btn-primary btn-sm"
id="plsRelaunchBtn"
onclick="ipcRenderer.invoke('$aura.pls.relaunchPls')"
id="aikariRelaunchBtn"
onclick="ipcRenderer.invoke('$aura.aikari.relaunchPls')"
>
重启进程
</button>

View File

@@ -6,6 +6,22 @@ global.__HUGO_AURA_UI_REACTIVES__.config = {
};
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(
@@ -30,6 +46,19 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
}
},
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"
@@ -77,7 +106,7 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
if (!side) {
setTimeout(() => {
global.__HUGO_AURA_LOADER__[
"Aura.UI.Assistant.Config.BehaviourCtrl.PlsStatus"
"Aura.UI.Assistant.Config.BehaviourCtrl.AikariStatus"
].active = false;
}, 500);
}
@@ -206,6 +235,11 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
inputEl.value = "";
inputEl.classList.remove("invalid");
inputEl.classList.remove("is-invalid");
global.__HUGO_AURA_UI_FUNCTIONS__.config.resetAuthDialogInputElOnSubmit(
global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword
);
acpDialogConfirmBtnEl.onclick = (_evt) => {
global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword();
};
@@ -214,6 +248,16 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
};
},
resetAuthDialogInputElOnSubmit: (func) => {
const inputEl = document.getElementById("acp-auth-user-input");
inputEl.onsubmit = (evt) => evt.preventDefault();
inputEl.onkeydown = (event) => {
if (event.key === "Enter") {
func();
}
};
},
handleACSNShow: async () => {
const acsnRootEl = document.getElementsByClassName(
"acp-config-status-notify"
@@ -265,6 +309,27 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
// 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");
}
},
};
(() => {
@@ -309,7 +374,8 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
let timeout = 0;
Array.from(operationElArr).forEach((el) => {
setTimeout(() => {
el.className = "operation-el-show aura-config-page-operation-el";
el.classList.remove("operation-el-hidden");
el.classList.add("operation-el-show");
}, timeout);
timeout += 150;
});
@@ -335,6 +401,9 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
"aura-config-page-header-area"
)[0];
acsDialogAreaEl.style = "";
global.__HUGO_AURA_UI_FUNCTIONS__.config.resetAuthDialogInputElOnSubmit(
global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword
);
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
acsDialogAreaEl.classList.remove("acp-ada-hidden");
acpAppBarEl.classList.add("color-reverse");
@@ -362,6 +431,7 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
};
const onMounted = () => {
global.__HUGO_AURA_UI_FUNCTIONS__.config.initCustomUIProps();
applyVersionInfo();
showAnimation();

View File

@@ -19,6 +19,11 @@
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;
}
@@ -33,7 +38,7 @@
}
.aura-config-page-header-area p {
margin-top: -1px;
margin-top: -2px;
}
.aura-config-page-header-area.header-collapsed {
@@ -48,3 +53,24 @@
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

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -1,670 +0,0 @@
if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig = {};
if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_REACTIVES__.subConfig = {};
(() => {
const REQUIRE_BASE = "../../aura/ui/pages/configSubPages/behaviourCtrl";
const IPC_METHOD_BASE = "$aura.pls";
const lifecycleStatus = {
installed: false,
detached: false,
svcInstalled: false,
svcRunning: false,
};
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus = {
toastAutoHideTimeout: null,
};
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>检查日志以获取详细信息</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_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"内核已删除",
null,
true,
true,
2000
);
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"内核删除失败",
"<p>检查日志以获取详细信息</p>",
true,
false,
null
);
}
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
};
} else {
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateOperationBtnStatus(
"Uninstall",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"info",
lifecycleStatus.svcRunning
? "正在停止服务并卸载"
: "正在请求卸载",
null,
false,
false,
null
);
if (lifecycleStatus.svcRunning) {
const stopRet = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "stopSvc" }
);
if (!stopRet.success) {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"服务卸载失败: 无法停止服务",
"<p>检查日志以获取详细信息</p><p>您可以尝试手动停止 PLS 服务</p>",
true,
false,
null
);
} else {
lifecycleStatus.svcRunning = false;
}
}
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "rmSvc" }
);
if (ret.success) {
lifecycleStatus.svcInstalled = false;
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"服务卸载成功",
null,
true,
true,
2000
);
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"服务卸载失败",
"<p>检查日志以获取详细信息</p>",
true,
false,
null
);
}
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
};
}
break;
case "Start":
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateOperationBtnStatus(
"Start",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"info",
"正在请求启动",
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();
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;
} else {
lifecycleStatus.installed = false;
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"请下载 PLS 内核以继续",
null,
true,
true,
3000
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
return;
}
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
);
}
},
downloadPLSBin: async () => {
const GLOBAL_FUNCTIONS =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus;
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "正在检查");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", true);
const CUR_CHANNEL = `${IPC_METHOD_BASE}.post.reportPlsDownloadStatus`;
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.ensurePlsInstallDir`);
GLOBAL_FUNCTIONS.updateToast(
"info",
"准备开始下载...",
null,
true,
true,
2000
);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "等待下载");
const callbackFn = (_evt, info) => {
switch (info.status) {
case "failed":
GLOBAL_FUNCTIONS.updateToast(
"error",
"下载失败",
`<p>${
info.message ? info.message : "检查日志以获取错误信息"
}</p>`,
true,
true,
5000
);
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
break;
case "done":
GLOBAL_FUNCTIONS.updateToast(
"success",
"下载成功",
null,
true,
true,
2500
);
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
true,
"下载内核"
);
lifecycleStatus.installed = true;
GLOBAL_FUNCTIONS.updateStatusContent();
break;
case "waiting":
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
true,
"等待中..."
);
break;
case "progressing":
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
true,
`下载 ${Math.round(info.progress)}%`
);
break;
}
};
ipcRenderer.on(CUR_CHANNEL, callbackFn);
ipcRenderer.invoke(`$aura.pls.downloadPls`, {
channel: "stable",
reportTo: "assistant",
});
},
};
const GLOBAL_FUNCTIONS =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus;
const {
updatePlsStatusFromLocal,
} = require(`${REQUIRE_BASE}/../../../composables/plsConfigManager`);
const initBsTooltip = () => {
const tooltipTriggerList = document.querySelectorAll(
'[data-bs-toggle="tooltip"]'
);
const _tooltipList = [...tooltipTriggerList].map(
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
);
};
const updateStatusEl = (
areaContainerId,
areaTextId,
target,
text = false
) => {
const areaContainerEl = document.getElementById(areaContainerId);
const areaContainerText = document.getElementById(areaTextId);
switch (target) {
case "PENDING":
areaContainerEl.className =
"acs-bc-pls-status-page-status-area pending";
break;
case "SUCCESS":
areaContainerEl.className =
"acs-bc-pls-status-page-status-area success";
break;
case "FAILED":
areaContainerEl.className = "acs-bc-pls-status-page-status-area failed";
break;
case "WARNING":
areaContainerEl.className =
"acs-bc-pls-status-page-status-area warning";
break;
case "INFO":
areaContainerEl.className = "acs-bc-pls-status-page-status-area info";
break;
default:
return false;
}
areaContainerText.textContent = text ? text : "";
return true;
};
const onMounted = () => {
initBsTooltip();
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.refreshPlsStatus(true);
const eventListener = () => {
GLOBAL_FUNCTIONS.updateStatusContent();
};
document.addEventListener("onPLSStatsUpdate", eventListener);
global.__HUGO_AURA_GLOBAL__.utils.createOnLeaveEvtListener(
"onPLSStatsUpdate",
eventListener
);
};
onMounted();
})();

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,6 +32,14 @@
}
)
.then(async (response) => {
if (response.status !== 200) {
resolve({
success: true,
data: null,
status: response.status,
});
}
const parsedData = await response.json();
resolve({
@@ -76,7 +84,7 @@
const diskElTemplate = document.createElement("p");
diskElTemplate.classList.add("acs-bc-dsc-fop-disk-el");
if (!curConfig.enable) {
if (!curConfig.enabled) {
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
if (disk.status !== 0) {
@@ -87,44 +95,16 @@
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;
let idx = 0;
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
if (curConfig.frozenDisks.includes(disk.name.toLowerCase())) {
// @ts-expect-error
curDiskEl.classList.add("active");
}
curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl);
idx += 1;
}
}
@@ -140,9 +120,10 @@
)[0];
const eventListener = (_event) => {
if (!global.__HUGO_AURA__.plsRules) return;
if (!global.__HUGO_AURA__.aikariRules) return;
composables.getAndUpdateDiskInfo(
global.__HUGO_AURA__.plsRules.client.security.uploadFreezeInfo
global.__HUGO_AURA__.aikariRules.ssaFeatures.securityPolicies
.freezeManagement.freezeDiskInfoPost
);
};
rootEl.addEventListener("onAssociateValueUpdated", eventListener);

View File

@@ -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

@@ -183,17 +183,130 @@ const authSettings = [
},
{
index: 2,
id: "screenLockAuthOverrideType",
type: "radio",
name: "覆写模式",
description: "选择一个认证覆写模式",
id: "fastfailScreenLock",
type: "switch",
name: "禁用屏幕锁",
description: "启用本功能后, 屏幕锁将完全无法使用, <b>请注意风险</b>",
restart: false,
reload: false,
associateVal: ["rewrite.vendor/screenLock.enabled"],
warning: true,
warningContent: "本功能存在极大的被发现风险, 启用前请自估风险",
associateVal: ["rewrite.vendor/screenLock.fastfail"],
auraIf: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.enabled;
},
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.fastfail;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].fastfail =
newVal;
},
},
{
index: 3,
id: "showDirectUnlock",
type: "switch",
name: '显示 "直接解锁" 按钮',
description: '启用后, 屏幕锁下方的解锁类型选择区域可选择 "直接解锁"',
restart: false,
reload: false,
warning: true,
warningContent: "本功能存在极大的被发现风险, 启用前请自估风险",
associateVal: [
"rewrite.vendor/screenLock.enabled",
"rewrite.vendor/screenLock.fastfail",
],
auraIf: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.enabled;
},
auraDisable: () => {
if (
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].fastfail
) {
return { value: true, tooltip: '关闭 "禁用屏幕锁" 以继续' };
}
return { value: false };
},
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.showDirectUnlock;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.rewrite[
"vendor/screenLock"
].showDirectUnlock = newVal;
},
},
{
index: 4,
id: "clickActBtnToExit",
type: "switch",
name: "连击紧急解锁",
description: '启用后, 连击 10 次 "激活码解锁" 按钮可紧急解锁',
restart: false,
reload: false,
tip: true,
tipTitle: "不建议关闭本功能, 至少给自己留条出路",
associateVal: [
"rewrite.vendor/screenLock.enabled",
"rewrite.vendor/screenLock.fastfail",
],
auraIf: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.enabled;
},
auraDisable: () => {
if (
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].fastfail
) {
return { value: true, tooltip: '关闭 "禁用屏幕锁" 以继续' };
}
return { value: false };
},
defaultValue: true,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.clickBtnToExit;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.rewrite[
"vendor/screenLock"
].clickBtnToExit = newVal;
},
},
{
index: 5,
id: "screenLockAuthOverrideType",
type: "radio",
name: "认证覆写模式",
description: "选择一个认证覆写模式, 或不修改认证策略",
restart: false,
reload: false,
associateVal: [
"rewrite.vendor/screenLock.enabled",
"rewrite.vendor/screenLock.fastfail",
],
auraIf: () => {
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
.enabled;
},
auraDisable: () => {
if (
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].fastfail
) {
return { value: true, tooltip: '关闭 "禁用屏幕锁" 以继续' };
}
return { value: false };
},
defaultValue: "none",
templates: ["customActivationCode", "none"],
templateLabels: ["自定义激活码", "不修改"],
@@ -208,7 +321,7 @@ const authSettings = [
},
},
{
index: 3,
index: 6,
id: "customActivationCode",
type: "input",
subType: "password",
@@ -221,6 +334,7 @@ const authSettings = [
associateVal: [
"rewrite.vendor/screenLock.enabled",
"rewrite.vendor/screenLock.authRewriteType",
"rewrite.vendor/screenLock.fastfail",
],
auraIf: () => {
return (
@@ -229,6 +343,14 @@ const authSettings = [
.authRewriteType === "customActivationCode"
);
},
auraDisable: () => {
if (
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].fastfail
) {
return { value: true, tooltip: '关闭 "禁用屏幕锁" 以继续' };
}
return { value: false };
},
defaultValue: "",
placeHolder: "留空表示不修改, 保留已设置值",
valueGetter: () => {

View File

@@ -0,0 +1,202 @@
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;
},
},
],
},
{
id: 1,
categoryName: "U 盘提示",
child: [
{
index: 0,
id: "switchUsbInsertPromptButton",
type: "switch",
name: '隐藏 U 盘插入提示悬浮窗的 "开始查杀" 按钮',
description: '启用后, "打开 U 盘" 将成为悬浮窗中的 Primary 按钮',
restart: true,
reload: false,
associateVal: [
"networkRewrite.appearance/switchUsbInsertPromptBtn.enabled",
],
auraIf: () => true,
defaultValue: false,
auraDisable: () => {
if (
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].mode === "hide" &&
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].enabled
) {
return { value: true, tooltip: '禁用 "隐藏 U 盘插入提示" 以继续' };
} else {
return { value: false };
}
},
valueGetter: () => {
return (
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].mode === "switch" &&
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].enabled
);
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
if (newVal === true) {
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].mode = "switch";
}
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].enabled = newVal;
},
},
{
index: 1,
id: "hideUsbInsertPrompt",
type: "switch",
name: "隐藏 U 盘插入提示",
description: "启用后, 插入 U 盘将不再显示悬浮窗",
restart: true,
reload: false,
associateVal: [
"networkRewrite.appearance/switchUsbInsertPromptBtn.enabled",
],
auraIf: () => true,
defaultValue: false,
auraDisable: () => {
if (
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].mode === "switch" &&
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].enabled
) {
return {
value: true,
tooltip:
'禁用 "隐藏 U 盘插入提示悬浮窗的 "开始查杀" 按钮" 以继续',
};
} else {
return { value: false };
}
},
valueGetter: () => {
return (
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].mode === "hide" &&
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].enabled
);
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
if (newVal === true) {
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].mode = "hide";
}
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/switchUsbInsertPromptBtn"
].enabled = newVal;
},
},
],
},
{
id: 1,
categoryName: "广告拦截",
child: [
{
index: 0,
id: "banAdBlockPrompt",
type: "switch",
name: "隐藏广告拦截悬浮窗",
description: "启用后, 管家检测到未拦截广告弹窗时, 将不会再显示悬浮窗",
restart: true,
reload: false,
warning: true,
warningContent:
'此功能不会完全禁用 "广告拦截" 功能, 已被拦截的广告弹窗依然会被拦截。如果您希望彻底禁用广告拦截, 请参阅 Aikari 的相关设置项 (WIP)',
associateVal: [],
auraIf: () => true,
defaultValue: false,
valueGetter: () => {
return global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/banAdBlockPrompt"
].enabled;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
global.__HUGO_AURA_CONFIG__.networkRewrite[
"appearance/banAdBlockPrompt"
].enabled = newVal;
},
},
],
},
];
module.exports = { uxAndAppearanceSettings };

View File

@@ -17,6 +17,20 @@
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"
@@ -26,7 +40,7 @@
type="button"
role="tab"
aria-controls="about-subpage"
aria-selected="true"
aria-selected="false"
>
关于项目
</button>
@@ -39,6 +53,12 @@
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"

View File

@@ -5,13 +5,21 @@
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(() => {

View File

@@ -96,12 +96,9 @@ const functions = {
const handleExit = async () => {
const result = await awaitCompletePromise;
console.debug(result);
if (result) {
console.debug("ret true");
return { valid: true };
} else {
console.debug("ret false");
const inputEl = document.getElementById("auraSettingsPasswd");
// @ts-expect-error
inputEl.value = "";
@@ -145,6 +142,10 @@ const functions = {
}
};
global.__HUGO_AURA_UI_FUNCTIONS__.config.resetAuthDialogInputElOnSubmit(
verifyPassword
);
// @ts-expect-error
acpDialogConfirmBtnEl.onclick = verifyPassword;
// @ts-expect-error
@@ -155,6 +156,27 @@ const functions = {
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 = [
@@ -170,8 +192,6 @@ const auraSettings = [
description: "启用后, Aura 设置 UI 需要输入密码才可访问",
restart: false,
reload: false,
tip: true,
tipTitle: "启用访问密码将自动加密配置文件",
associateVal: null,
auraIf: () => true,
defaultValue: false,
@@ -288,8 +308,171 @@ const auraSettings = [
},
{
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: [],
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);
},
},
],
},
];

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 };

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;
}

View File

@@ -1,5 +1,65 @@
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 = (revive = false) => {
if (
!global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.showEntryIcon &&
!revive
) {
const rootEl = document.getElementById("root");
rootEl.classList.add("aura-header-icon-hidden");
}
if (
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
.fallbackAccessMethods.hotkey &&
!revive
) {
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();
document.addEventListener(
"onLoaderElRevive:Aura.UI.Assistant.HeaderEntry",
() => {
onMounted(true);
}
);
})();

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

40
src/aura/utils/pls.js Normal file
View File

@@ -0,0 +1,40 @@
// @ts-check
/**
* @param {Electron} electron
*/
const createWsWindow = (electron) => {
const path = require("path");
const { BrowserWindow } = electron;
const window = new BrowserWindow({
width: 0,
height: 0,
frame: false,
skipTaskbar: true,
transparent: true,
alwaysOnTop: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
devTools: true,
},
});
window.setIgnoreMouseEvents(true);
window.minimize();
window.loadFile(
path.join(
__dirname,
"..",
"ui",
"pages",
"windows",
"auraWsKeepAlive",
"index.html"
)
);
return window;
};
module.exports = { createWsWindow };

5
src/aura/utils/string.js Normal file
View File

@@ -0,0 +1,5 @@
const checkIfNonAscii = (str) => {
return Buffer.byteLength(str) !== str.length;
};
module.exports = { checkIfNonAscii };

View File

@@ -6,6 +6,7 @@ if (!global.__HUGO_AURA__) {
configInit: false,
central: () => {},
ipcInit: false,
auraDir: "",
plsStats: null,
plsSettings: null,
plsRules: null,
@@ -19,9 +20,15 @@ if (!global.__HUGO_AURA__) {
if (!global.__HUGO_AURA_API__) {
/** @type {import("../aura/types/shared/global").GlobalHugoAuraApiInfo} */
const __HUGO_AURA_API__ = {
baseUrl: "https://api-aura.xwx.li",
plsUpdate: "/api/v1/getPLSLatestVersion",
auraUpdate: "/api/v1/getAuraLatestVersion",
domains: [
"https://api-aura-projekts.delta.ooo",
"https://api-aura.asaka.site",
"https://api.hugoaura.dpdns.org",
"https://api-aura-projekts.minorice.moe",
"https://api.aura.vim.moe",
],
aikariUpdate: "/api/getAikariLatestVersion",
auraUpdate: "/api/getAuraLatestVersion",
};
global.__HUGO_AURA_API__ = __HUGO_AURA_API__;
}
@@ -30,8 +37,6 @@ if (!global.__HUGO_AURA_CONFIG__) {
global.__HUGO_AURA_CONFIG__ = {};
}
const fs = require("fs");
const util = require("util");
const path = require("path");
const os = require("os");
@@ -40,71 +45,45 @@ const RendererHooksManager = require("../aura/init/rendererHook/uiHooksManager")
const EventBus = require("../aura/utils/eventBus");
const NetworkHook = require("../aura/init/rendererHook/networkHook");
const ConfigManager = require("../aura/init/shared/configManager");
const RegistryManager = require("../aura/init/shared/registryManager");
const { buildIpcMain } = require("../aura/init/main/ipcHandler");
const plsUtils = require("../aura/utils/pls");
const stringUtils = require("../aura/utils/string");
/**
*
* @param {import("../aura/types/main/core").WindowName} windowName
*/
const initLogger = (windowName) => {
const logDir = path.join(os.homedir(), "Documents", "HugoAura", "logs");
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
const { initLogger } = require("../aura/init/main/logger");
const getUserDocumentsDirPath = () => {
const registryManager = new RegistryManager();
const pathInfo = registryManager.readRegKeySync(
'"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders"',
"Personal",
false,
true,
/REG_EXPAND_SZ\s+(.+)/
);
try {
if (pathInfo.success && pathInfo.data) {
const resolvedPath = pathInfo.data.replace(
/%([^%]+)%/g,
(match, varName) => {
return process.env[varName] || match;
}
);
if (stringUtils.checkIfNonAscii(resolvedPath)) {
console.warn("[HugoAura / Init] Detected non-ASCII char in resolved user personal folder: ", resolvedPath);
throw new Error("Non-ASCII char detected");
}
return resolvedPath;
} else {
throw new Error("Registry data failed to get");
}
} catch (err) {
console.error(
"[HugoAura / Init / Logger] Failed to get the path of documents dir, using default val. | Error: ", err
);
return path.join(os.homedir(), "Documents");
}
const logFile = path.join(
logDir,
`main-${windowName}-${new Date().toISOString().replace(/:/g, "-")}.log`
);
const logStream = fs.createWriteStream(logFile, { flags: "a" });
const originalConsole = {
log: console.log,
error: console.error,
warn: console.warn,
info: console.info,
debug: console.debug,
};
console.log = function (...args) {
const msg = util.format("[LOG] ", ...args) + "\n";
logStream.write(msg);
originalConsole.log.apply(console, args);
};
console.error = function (...args) {
const msg = util.format("[ERROR] ", ...args) + "\n";
logStream.write(msg);
originalConsole.error.apply(console, args);
};
console.warn = function (...args) {
const msg = util.format("[WARN] ", ...args) + "\n";
logStream.write(msg);
originalConsole.warn.apply(console, args);
};
console.info = function (...args) {
const msg = util.format("[INFO] ", ...args) + "\n";
logStream.write(msg);
originalConsole.info.apply(console, args);
};
console.debug = function (...args) {
if (!process.argv.includes("--aura-debug")) return;
const msg = util.format("[DEBUG] ", ...args) + "\n";
logStream.write(msg);
originalConsole.debug.apply(console, args);
};
process.on("uncaughtException", (err) => {
console.error("UNCAUGHT EXCEPTION:", err);
});
console.log(
"[HugoAura / Init / Logger] Logger initialized. Log file:",
logFile
);
};
/**
@@ -128,6 +107,11 @@ const launcher = ({ central, windowName, config }) => {
app.exit(0);
};
global.__HUGO_AURA__.auraDir = path.join(
getUserDocumentsDirPath(),
"HugoAura"
);
// >>> Init Logger <<< //
initLogger(windowName);
@@ -141,6 +125,7 @@ const launcher = ({ central, windowName, config }) => {
// >>> Init Config <<< //
const configManager = new ConfigManager();
configManager.side = "main";
configManager.migrateOldConfigFile();
configManager.ensureConfigExists();
const loadedConfig = configManager.loadConfig();
if (!global.__HUGO_AURA__.configInit) global.__HUGO_AURA__.configInit = true;
@@ -174,6 +159,13 @@ const launcher = ({ central, windowName, config }) => {
config.canOpenDevTool = true;
}
// >>> Create WebSocket KeepAlive Window <<< //
if (!global.__HUGO_AURA__.hookedWindows?.has("auraWsKeepAlive")) {
const wsKaWin = plsUtils.createWsWindow(electron);
// @ts-expect-error
global.__HUGO_AURA__.hookedWindows.set("auraWsKeepAlive", wsKaWin);
}
// >>> Listeners <<< //
/**

View File

@@ -1,13 +1,18 @@
// @ts-check
const __AURA_VERSION__ = "0.1.1-pre-IV";
const __AURA_VERSION__ = "0.2.0-rc1-p3";
(() => {
if (require.main) return; // 如果只是导入 Aura Version, 不运行闭包逻辑
const auraDir = require("electron").ipcRenderer.sendSync(
"$aura.base.getAuraDirSync"
);
// >>> Init Global Vars <<< //
if (!global.__HUGO_AURA__) {
global.__HUGO_AURA__ = {
auraDir: auraDir.data,
configInit: true, // preload 始终比 hook 晚, 默认 config 已初始化
// ↑ 保留此参数的目的 -> 用于 configManager 中, configManager 的行为在 Renderer 和 Main 中是一致的
version: __AURA_VERSION__,