mirror of
https://github.com/HugoAura/Seewo-HugoAura.git
synced 2026-06-20 23:14:28 +08:00
Compare commits
61 Commits
v0.1.1-pre
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9622c28ad | ||
|
|
8a44af078a | ||
|
|
c5c63c6639 | ||
|
|
420f35a5e4 | ||
|
|
1031bf04bb | ||
|
|
db55f54cc1 | ||
|
|
46ca11caad | ||
|
|
6ee3f99411 | ||
|
|
f2bb2ab06a | ||
|
|
971d3db43a | ||
|
|
f7feca7ff2 | ||
|
|
b27b1a6573 | ||
|
|
1b90932869 | ||
|
|
c84aaef994 | ||
|
|
8656fd0334 | ||
|
|
80354cc310 | ||
|
|
cd946a60fb | ||
|
|
a6c68782a0 | ||
|
|
d6db3e398a | ||
|
|
7afeb39275 | ||
|
|
b277a61923 | ||
|
|
08290301a3 | ||
|
|
5a29e909f2 | ||
|
|
2b9d1c97c5 | ||
|
|
abd5a10d24 | ||
|
|
c857f00771 | ||
|
|
6b64481e2e | ||
|
|
fb01b27d1d | ||
|
|
585f1713a9 | ||
|
|
2ff4a28b70 | ||
|
|
d5b4c4b61e | ||
|
|
774d999752 | ||
|
|
ae2e2ff62d | ||
|
|
b8f27d9f7c | ||
|
|
807ac913da | ||
|
|
f6a30351fd | ||
|
|
0494394fd8 | ||
|
|
6be31652f9 | ||
|
|
a638a29cc6 | ||
|
|
dae0f033a5 | ||
|
|
bd2f2e4154 | ||
|
|
290cbfed53 | ||
|
|
9a2a335742 | ||
|
|
e63c989d88 | ||
|
|
70ffa3f581 | ||
|
|
c0249693a8 | ||
|
|
ca5d94ebd8 | ||
|
|
a9d3772b51 | ||
|
|
9e63a9374f | ||
|
|
c3a70ece88 | ||
|
|
54df7011c0 | ||
|
|
423142d785 | ||
|
|
a639ae0ac6 | ||
|
|
ed29f1b86f | ||
|
|
8ab55bc93c | ||
|
|
50bde49414 | ||
|
|
7b7254f1bb | ||
|
|
6da8348b41 | ||
|
|
839afa79e8 | ||
|
|
7c8d3d4fbc | ||
|
|
fbc5cf1f57 |
18
.github/ISSUE_TEMPLATE/bugReport.yml
vendored
18
.github/ISSUE_TEMPLATE/bugReport.yml
vendored
@@ -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
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1 +1 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
|
||||
13
.github/ISSUE_TEMPLATE/featureRequest.yml
vendored
13
.github/ISSUE_TEMPLATE/featureRequest.yml
vendored
@@ -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
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/other.yml
vendored
2
.github/ISSUE_TEMPLATE/other.yml
vendored
@@ -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
|
||||
|
||||
87
.github/workflows/pack.yml
vendored
Normal file
87
.github/workflows/pack.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
name: Pack source code & Create CI/CD Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev, stable]
|
||||
pull_request:
|
||||
branches: [dev, stable, main, master]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
pack:
|
||||
name: Pack
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: "./HugoAura-Code"
|
||||
|
||||
- name: Create aura.zip
|
||||
run: |
|
||||
echo "[DEBUG] Creating artifacts directory"
|
||||
mkdir -p Artifacts
|
||||
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: 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
|
||||
26
.github/workflows/trigger-install-upd.yml
vendored
Normal file
26
.github/workflows/trigger-install-upd.yml
vendored
Normal 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 }}"
|
||||
}
|
||||
57
.gitignore
vendored
Normal file → Executable file
57
.gitignore
vendored
Normal file → Executable file
@@ -1,29 +1,28 @@
|
||||
# Dependencies
|
||||
|
||||
node_modules/
|
||||
.npm
|
||||
.yarn
|
||||
|
||||
# ESLint Cache
|
||||
|
||||
.eslintcache
|
||||
|
||||
# NYC Test Output
|
||||
|
||||
.nyc_output
|
||||
|
||||
# Build Artifacts
|
||||
|
||||
/dist
|
||||
/buikd
|
||||
|
||||
# dotEnv Files
|
||||
|
||||
.env
|
||||
.env*
|
||||
|
||||
# Misc
|
||||
|
||||
.DS_Store
|
||||
*.log
|
||||
*.bak
|
||||
# SSA Defaults
|
||||
|
||||
_extraResources
|
||||
addons/
|
||||
app.asar/addons
|
||||
app.asar/assets
|
||||
app.asar/config
|
||||
app.asar/node_modules
|
||||
app.asar/public
|
||||
app.asar/main.js
|
||||
app.asar/package.json
|
||||
assets/
|
||||
|
||||
autoConfiguration.json
|
||||
|
||||
# NPM Packages
|
||||
node_modules/
|
||||
package-lock.json
|
||||
|
||||
# OS Files
|
||||
desktop.ini
|
||||
.DS_Store
|
||||
|
||||
# Editor Files
|
||||
.vscode/
|
||||
|
||||
# Misc
|
||||
*.bak
|
||||
|
||||
51
README.md
51
README.md
@@ -1,20 +1,41 @@
|
||||
<center><img src="https://s2.loli.net/2025/04/18/IpZL7qMw2KFEi8o.png" /></center>
|
||||
|
||||
|
||||
<h1 align="center">HugoAura</h1>
|
||||
<h4 align="center">下一代希沃管家注入式修改方案</h4>
|
||||
<div align="center">
|
||||
<a href="https://github.com/HugoAura/HugoAura">首页</a> · <a href="https://hugoaura.org/about">关于</a> · <a href="https://github.com/HugoAura/HugoAura/wiki">文档</a> · <a href="https://github.com/HugoAura/HugoAura/issues">反馈</a>
|
||||
<a href="https://github.com/HugoAura/HugoAura">首页</a> · <a href="https://hugo.aurax.cc/about">关于 (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.3822
|
||||
> 已经过测试的希沃管家版本: 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)
|
||||
|
||||

|
||||
|
||||
## ✨ 概览
|
||||
|
||||
@@ -32,8 +53,8 @@
|
||||
|
||||
- [x] 修改希沃管家密码认证组件 (自定义密码 / 解除密码 / 重设认证方式 / ...)
|
||||
- [x] 阻止希沃管家前端 Audit 上报行为
|
||||
- [ ] Aura 代理层服务 (篡改上报数据 / 欺骗冰冻状态)
|
||||
- [ ] 屏蔽屏幕锁 / 自定义屏幕锁行为
|
||||
- [x] 屏蔽屏幕锁 / 自定义屏幕锁行为
|
||||
- [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 声明
|
||||
|
||||

|
||||
|
||||
## 📦 贡献准则
|
||||
|
||||

|
||||
|
||||
## 🎉 鸣谢
|
||||
|
||||

|
||||
|
||||
## ❗ 免责声明
|
||||
|
||||
本项目仅用于研究或教育目的, 请勿将本项目用于可能违反当地法律、侵犯著作权或其他软件 EULA 的用途。若将本项目用于非法用途, 一切后果由使用者承担。开发者不承担此类行为带来的任何后果或责任。
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
package.json
Executable file
20
package.json
Executable file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "HugoAura",
|
||||
"version": "0.2.0-rc1-p3",
|
||||
"description": "Aura for SeewoHugo",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"electron": "^36.3.2"
|
||||
},
|
||||
"scripts": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/HugoAura/Seewo-HugoAura.git"
|
||||
},
|
||||
"author": "Minoricew",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://github.com/HugoAura/Seewo-HugoAura/issues"
|
||||
},
|
||||
"homepage": "https://github.com/HugoAura/Seewo-HugoAura"
|
||||
}
|
||||
@@ -11,7 +11,7 @@ const buildIpcMain = (electron) => {
|
||||
/**
|
||||
* @type {import("../../types/main/electron").AuraIPCMain}
|
||||
*/
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
const ipcMain = electron.ipcMain;
|
||||
|
||||
/**
|
||||
@@ -28,11 +28,33 @@ const buildIpcMain = (electron) => {
|
||||
* @param {string} chan
|
||||
* @param {any} targetData
|
||||
*/
|
||||
if (!global.__HUGO_AURA__.hookedWindows) {
|
||||
return {
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
const sendDataToWebContents = (key, chan, targetData) => {
|
||||
const webContents =
|
||||
global.__HUGO_AURA__.hookedWindows.get(key).webContents;
|
||||
// @ts-expect-error
|
||||
global.__HUGO_AURA__.hookedWindows.get(key)?.webContents;
|
||||
|
||||
if (grep !== webContents) webContents.send(chan, targetData);
|
||||
if (!webContents) {
|
||||
console.error(
|
||||
`[HugoAura / Main / IPC / ERROR] Failed sending data to ${key}: WebContents not found`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (grep !== webContents) {
|
||||
webContents.send(chan, targetData);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
};
|
||||
|
||||
if (windowKey === "*") {
|
||||
@@ -42,23 +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 { applyPlsIpcHandler } = require("./ipcModules/plsIpcHandler");
|
||||
const { applyBaseIpcHandler } = require("./ipcModules/baseIpcHandler");
|
||||
const { applyDebugIpcHandler } = require("./ipcModules/debugIpcHandler");
|
||||
const { applyConfigIpcHandler } = require("./ipcModules/configIpcHandler");
|
||||
const { applyFsIpcHandler } = require("./ipcModules/fsIpcHandler");
|
||||
const { applyAikariIpcHandler } = require("./ipcModules/aikariIpcHandler");
|
||||
|
||||
ipcMain.handle("$aura.base.restartApplication", async () => {
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
});
|
||||
|
||||
applyPlsIpcHandler(ipcMain);
|
||||
applyBaseIpcHandler(ipcMain);
|
||||
applyDebugIpcHandler(ipcMain);
|
||||
applyConfigIpcHandler(ipcMain);
|
||||
applyFsIpcHandler(ipcMain);
|
||||
applyAikariIpcHandler(ipcMain);
|
||||
};
|
||||
|
||||
module.exports = { buildIpcMain };
|
||||
|
||||
915
src/aura/init/main/ipcModules/aikariIpcHandler.js
Normal file
915
src/aura/init/main/ipcModules/aikariIpcHandler.js
Normal 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 };
|
||||
73
src/aura/init/main/ipcModules/baseIpcHandler.js
Normal file
73
src/aura/init/main/ipcModules/baseIpcHandler.js
Normal 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 };
|
||||
79
src/aura/init/main/ipcModules/configIpcHandler.js
Executable file
79
src/aura/init/main/ipcModules/configIpcHandler.js
Executable file
@@ -0,0 +1,79 @@
|
||||
// @ts-check
|
||||
|
||||
const __SCOPE = "main";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import("../../../types/main/electron").AuraIPCMain} ipcMain
|
||||
*/
|
||||
const applyConfigIpcHandler = (ipcMain) => {
|
||||
const methodBase = "$aura.config";
|
||||
|
||||
const mainEventBus = global.__HUGO_AURA_EVENT_BUS__;
|
||||
|
||||
const ConfigManager = require("../../shared/configManager");
|
||||
const configManager = global.__HUGO_AURA_CONFIG_MGR__
|
||||
? global.__HUGO_AURA_CONFIG_MGR__
|
||||
: new ConfigManager();
|
||||
|
||||
ipcMain.on(`${methodBase}.refreshMainConfig`, (_event) => {
|
||||
mainEventBus.emit("$aura.config.refreshConfig");
|
||||
});
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.setConfigEncSettings`,
|
||||
(
|
||||
/** @type {import("electron").IpcMainInvokeEvent} */ _event,
|
||||
/** @type {{ target: boolean }} */ arg
|
||||
) => {
|
||||
mainEventBus.emit("$aura.config.updateConfigEncSettings", arg.target);
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.on(`${methodBase}.getConfigFromMainSync`, (event, _arg) => {
|
||||
if (
|
||||
global.__HUGO_AURA_CONFIG__ &&
|
||||
Object.keys(global.__HUGO_AURA_CONFIG__).length !== 0
|
||||
) {
|
||||
event.returnValue = {
|
||||
success: true,
|
||||
data: global.__HUGO_AURA_CONFIG__,
|
||||
};
|
||||
} else {
|
||||
console.warn(
|
||||
"[HugoAura / Main / IPC / Config / WARN] Global config var not found!"
|
||||
);
|
||||
event.returnValue = {
|
||||
success: false,
|
||||
data: {},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.dispatchConfigFromRenderer`,
|
||||
(_event, /** @type {{data: string, writeConfig?: boolean}} */ arg) => {
|
||||
const parsedData = JSON.parse(arg.data);
|
||||
|
||||
global.__HUGO_AURA_CONFIG__ = parsedData;
|
||||
|
||||
if (arg.writeConfig) {
|
||||
const result = configManager.writeConfig(parsedData);
|
||||
if (!result) {
|
||||
return {
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = { applyConfigIpcHandler };
|
||||
13
src/aura/init/main/ipcModules/debugIpcHandler.js
Normal file
13
src/aura/init/main/ipcModules/debugIpcHandler.js
Normal 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 };
|
||||
207
src/aura/init/main/ipcModules/fsIpcHandler.js
Executable file
207
src/aura/init/main/ipcModules/fsIpcHandler.js
Executable file
@@ -0,0 +1,207 @@
|
||||
// @ts-check
|
||||
|
||||
const __SCOPE = "main";
|
||||
|
||||
const { exec } = require("child_process");
|
||||
const nodeHttp = require("http");
|
||||
const nodeHttps = require("https");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const { genRandomHex } = require("../../../utils/crypto");
|
||||
|
||||
const composableFunctions = {
|
||||
/**
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} targetPath
|
||||
* @param {((arg: DownloadTask) => any)} progressCallback
|
||||
*/
|
||||
downloadFile: async (url, targetPath, progressCallback) => {
|
||||
if (!progressCallback) return false;
|
||||
const taskId = genRandomHex();
|
||||
|
||||
/**
|
||||
* @type {DownloadTask}
|
||||
*/
|
||||
const failedTemplate = {
|
||||
id: taskId,
|
||||
progress: 100,
|
||||
status: "failed",
|
||||
dlUrl: url,
|
||||
savePath: targetPath,
|
||||
message: "",
|
||||
};
|
||||
|
||||
if (!url || !targetPath) {
|
||||
failedTemplate.message = "Invalid arg";
|
||||
progressCallback(failedTemplate);
|
||||
return false;
|
||||
}
|
||||
|
||||
const dirName = path.dirname(targetPath);
|
||||
|
||||
if (!fs.existsSync(dirName)) {
|
||||
fs.mkdirSync(dirName, { recursive: true });
|
||||
}
|
||||
|
||||
const httpModuleIns = url.startsWith("https") ? nodeHttps : nodeHttp;
|
||||
|
||||
global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, {
|
||||
status: "waiting",
|
||||
cancelReq: null,
|
||||
});
|
||||
|
||||
progressCallback({
|
||||
id: taskId,
|
||||
progress: 0,
|
||||
status: "waiting",
|
||||
dlUrl: url,
|
||||
savePath: targetPath,
|
||||
});
|
||||
|
||||
const fsStream = fs.createWriteStream(targetPath);
|
||||
|
||||
const dlReq = httpModuleIns.get(url, (response) => {
|
||||
if (response.statusCode !== 200) {
|
||||
fsStream.close();
|
||||
failedTemplate.message = `Request error: HTTP ${response.statusCode}`;
|
||||
progressCallback(failedTemplate);
|
||||
return false;
|
||||
}
|
||||
|
||||
const contentLength = response.headers["content-length"];
|
||||
// @ts-expect-error
|
||||
const totalBytes = parseInt(contentLength, 10) || 0; // No error handling 😆
|
||||
let curRecvBytes = 0;
|
||||
|
||||
let hasCancelled = false;
|
||||
|
||||
global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, {
|
||||
status: "progressing",
|
||||
cancelReq: () => {
|
||||
hasCancelled = true;
|
||||
dlReq.destroy();
|
||||
fsStream.close();
|
||||
fs.unlink(targetPath, () => {});
|
||||
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
|
||||
progressCallback({
|
||||
id: taskId,
|
||||
progress: 100,
|
||||
curBytes: curRecvBytes,
|
||||
totalBytes: totalBytes,
|
||||
status: "cancelled",
|
||||
dlUrl: url,
|
||||
savePath: targetPath,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
response.on("data", (chunk) => {
|
||||
curRecvBytes += chunk.length;
|
||||
const curProgress =
|
||||
totalBytes > 0 ? (curRecvBytes / totalBytes) * 100 : 0;
|
||||
progressCallback({
|
||||
id: taskId,
|
||||
progress: curProgress.toFixed(2),
|
||||
curBytes: curRecvBytes,
|
||||
totalBytes: totalBytes,
|
||||
status: "progressing",
|
||||
dlUrl: url,
|
||||
savePath: targetPath,
|
||||
});
|
||||
});
|
||||
|
||||
response.pipe(fsStream);
|
||||
|
||||
fsStream.on("finish", () => {
|
||||
fsStream.close();
|
||||
if (hasCancelled) {
|
||||
return;
|
||||
}
|
||||
progressCallback({
|
||||
id: taskId,
|
||||
progress: (100).toFixed(2),
|
||||
curBytes: curRecvBytes,
|
||||
totalBytes: totalBytes,
|
||||
status: "done",
|
||||
dlUrl: url,
|
||||
savePath: targetPath,
|
||||
});
|
||||
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
dlReq.on("error", (e) => {
|
||||
fsStream.close();
|
||||
fs.unlink(targetPath, () => {});
|
||||
failedTemplate.message =
|
||||
"Request error: Unexpected error while downloading file";
|
||||
failedTemplate.errorObj = e;
|
||||
console.error(
|
||||
`[HugoAura / IPC / FS / ERROR] Error downloading file from ${url}, errorObj:`,
|
||||
e
|
||||
);
|
||||
progressCallback(failedTemplate);
|
||||
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import("electron").IpcMain} ipcMain
|
||||
*/
|
||||
const applyFsIpcHandler = (ipcMain) => {
|
||||
const methodBase = "$aura.fs";
|
||||
|
||||
global.__HUGO_AURA__.fsTasks = {
|
||||
downloadTasks: new Map(),
|
||||
};
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.dl.cancelDownloadTask`,
|
||||
/**
|
||||
*
|
||||
* @param {import("electron").IpcMainInvokeEvent} _evt
|
||||
* @param {{ targetTaskId: string }} arg
|
||||
* @returns {{ success: boolean, error: string | null }}
|
||||
*/
|
||||
(_evt, arg) => {
|
||||
if (!arg.targetTaskId) {
|
||||
return {
|
||||
success: false,
|
||||
error: "ARG_INVALID",
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.__HUGO_AURA__.fsTasks?.downloadTasks.has(arg.targetTaskId)) {
|
||||
return {
|
||||
success: false,
|
||||
error: "TASK_ID_NOT_FOUND",
|
||||
};
|
||||
}
|
||||
|
||||
const taskObj = global.__HUGO_AURA__.fsTasks.downloadTasks.get(
|
||||
arg.targetTaskId
|
||||
);
|
||||
if (!taskObj?.cancelReq) {
|
||||
return {
|
||||
success: false,
|
||||
error: "TASK_NOT_STARTED",
|
||||
};
|
||||
}
|
||||
|
||||
taskObj.cancelReq();
|
||||
return {
|
||||
success: true,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = { fsComposables: composableFunctions, applyFsIpcHandler };
|
||||
@@ -1,190 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
const __SCOPE = "main";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import("../../../types/main/electron").AuraIPCMain} ipcMain
|
||||
*/
|
||||
const applyPlsIpcHandler = (ipcMain) => {
|
||||
const methodBase = "$aura.pls";
|
||||
|
||||
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}.getPlsFolderExists`,
|
||||
/**
|
||||
*
|
||||
* @returns {{ success: boolean; data: { isExists: boolean }; error?: Error }}
|
||||
*/
|
||||
(_event, _arg) => {
|
||||
const plsFolderPath = path.join(__dirname, "../../../proxy");
|
||||
try {
|
||||
const result = fs.existsSync(plsFolderPath);
|
||||
return {
|
||||
success: true,
|
||||
data: { isExists: result },
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
success: false,
|
||||
data: { isExists: false },
|
||||
error: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.getPlsStats`,
|
||||
/**
|
||||
*
|
||||
* @returns {{ success: boolean; data: PLSStatus; }}
|
||||
*/
|
||||
(_event, _arg) => {
|
||||
return {
|
||||
success: true,
|
||||
data: global.__HUGO_AURA__.plsStats,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.updatePlsStats`,
|
||||
/**
|
||||
*
|
||||
* @param {import("electron").IpcMainInvokeEvent} _event
|
||||
* @param {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> }}
|
||||
*/
|
||||
(_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;
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.getPlsRules`,
|
||||
/**
|
||||
*
|
||||
* @returns {{ success: boolean; data: Record<any, any> }}
|
||||
*/
|
||||
(_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;
|
||||
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,
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = { applyPlsIpcHandler };
|
||||
114
src/aura/init/main/logger.js
Normal file
114
src/aura/init/main/logger.js
Normal 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 };
|
||||
82
src/aura/init/main/windowHooksManager.js
Executable file
82
src/aura/init/main/windowHooksManager.js
Executable file
@@ -0,0 +1,82 @@
|
||||
// @ts-check
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
class WindowHooksManager {
|
||||
loadHooks() {
|
||||
if (
|
||||
global.__HUGO_AURA__.windowHooks &&
|
||||
Object.keys(global.__HUGO_AURA__.windowHooks).length !== 0
|
||||
) {
|
||||
return global.__HUGO_AURA__.windowHooks;
|
||||
}
|
||||
|
||||
const hooksPath = path.join(__dirname, "../../../aura/mainProcess/hooks");
|
||||
|
||||
/** @type {import("../../types/main/core").UIHooksMap} */
|
||||
const hooks = new Map();
|
||||
|
||||
try {
|
||||
const files = fs.readdirSync(hooksPath);
|
||||
files.forEach((file) => {
|
||||
if (!file.endsWith(".js")) return;
|
||||
|
||||
try {
|
||||
const hook = require(path.join(hooksPath, file));
|
||||
/** @type {import("../../types/main/core").WindowName} */
|
||||
const targetWindow = hook.windowName || path.basename(file, ".js");
|
||||
hooks.set(targetWindow, hook);
|
||||
console.log(
|
||||
`[HugoAura / Init / WDH] Loaded main process hook for window: ${targetWindow}`
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[HugoAura / Init / WDH / Error] Failed to load main process hook ${file}:`,
|
||||
err
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Init / WDH / Error] Failed to read hooks directory:",
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
global.__HUGO_AURA__.windowHooks = hooks;
|
||||
return hooks;
|
||||
}
|
||||
|
||||
initHookForWindow(
|
||||
windowName,
|
||||
centralIns,
|
||||
mainProcessAppInstance,
|
||||
browserWindowInstance
|
||||
) {
|
||||
const stripWindowName = windowName.split("_")[0];
|
||||
|
||||
if (!global.__HUGO_AURA__.windowHooks) return;
|
||||
|
||||
if (!global.__HUGO_AURA__.windowHooks.has(stripWindowName)) {
|
||||
console.log(
|
||||
`[HugoAura / Init / WDH] Window ${windowName} has no corresponding main process hooks, ignoring...`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const { hookFunc } = global.__HUGO_AURA__.windowHooks.get(stripWindowName);
|
||||
hookFunc(
|
||||
centralIns,
|
||||
mainProcessAppInstance,
|
||||
browserWindowInstance,
|
||||
windowName
|
||||
);
|
||||
|
||||
console.log(
|
||||
`[HugoAura / Init / WDH / Success / ${windowName}] Main process hook initialized.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WindowHooksManager;
|
||||
@@ -1,4 +1,4 @@
|
||||
const path = require('path');
|
||||
const path = require("path");
|
||||
|
||||
class WebpackHook {
|
||||
#ruleCache = new Map();
|
||||
@@ -19,7 +19,7 @@ class WebpackHook {
|
||||
if (!rule) {
|
||||
rule = require(path.join(
|
||||
__dirname,
|
||||
'../../../aura/jsRewrite/',
|
||||
"../../../aura/jsRewrite/",
|
||||
rulePath
|
||||
));
|
||||
this.#ruleCache.set(rulePath, rule);
|
||||
@@ -49,7 +49,7 @@ class WebpackHook {
|
||||
|
||||
patchModules(modules, rewrites) {
|
||||
modules.forEach((mod, index) => {
|
||||
if (typeof mod !== 'function') return;
|
||||
if (typeof mod !== "function") return;
|
||||
const stringifyFunc = mod.toString();
|
||||
|
||||
rewrites.forEach((rewrite) => {
|
||||
@@ -67,14 +67,14 @@ class WebpackHook {
|
||||
let rewrittenFunction = mod;
|
||||
|
||||
switch (method) {
|
||||
case 'reactComponent':
|
||||
case "reactComponent":
|
||||
window.__HUGO_AURA_HOOK__[ruleId] = {
|
||||
feature: rewrite.feature,
|
||||
newFunction: rewrite.newFunction
|
||||
}
|
||||
newFunction: rewrite.newFunction,
|
||||
};
|
||||
rewrittenFunction = rewrite.preHook(mod);
|
||||
break;
|
||||
case 'legacy':
|
||||
case "legacy":
|
||||
default:
|
||||
rewrittenFunction = rewrite.newFunction;
|
||||
break;
|
||||
@@ -95,7 +95,7 @@ class WebpackHook {
|
||||
});
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof window !== "undefined") {
|
||||
window.__HUGO_AURA_DEBUG__ = {
|
||||
getRuleCache: () => Array.from(this.#ruleCache.keys()),
|
||||
};
|
||||
@@ -105,7 +105,7 @@ class WebpackHook {
|
||||
installHook(window, config) {
|
||||
let realWebpackJsonp = window.webpackJsonp;
|
||||
|
||||
Object.defineProperty(window, 'webpackJsonp', {
|
||||
Object.defineProperty(window, "webpackJsonp", {
|
||||
get: () => realWebpackJsonp,
|
||||
set: (value) => {
|
||||
console.log(
|
||||
@@ -119,7 +119,7 @@ class WebpackHook {
|
||||
if (args[0] && Array.isArray(args[0][1])) {
|
||||
const [chunkIds, modules] = args[0];
|
||||
console.log(
|
||||
`[HugoAura / AppHook] Intercepting chunk ${chunkIds.join(', ')}`
|
||||
`[HugoAura / AppHook] Intercepting chunk ${chunkIds.join(", ")}`
|
||||
);
|
||||
|
||||
const rewrites = this.loadRewriteRules(config);
|
||||
|
||||
@@ -24,40 +24,6 @@
|
||||
});
|
||||
};
|
||||
|
||||
const createStore = () => {
|
||||
const store = {
|
||||
router: "/",
|
||||
};
|
||||
const internal = {
|
||||
get: (key) => store[key],
|
||||
set: (key, value) => {
|
||||
store[key] = value;
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
window.$store = {
|
||||
get: (key) => {
|
||||
const stack = new Error().stack;
|
||||
if (stack.includes("aura/ui/pages/")) {
|
||||
return internal.get(key);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
set: (key, value) => {
|
||||
const stack = new Error().stack;
|
||||
if (stack.includes("aura/ui/pages/")) {
|
||||
return internal.set(key, value);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
return internal;
|
||||
};
|
||||
|
||||
const store = createStore();
|
||||
|
||||
const createUILoader = () => {
|
||||
const modules = "__TEMPLATE_TARGETS__";
|
||||
const containers = new Map();
|
||||
@@ -65,6 +31,63 @@
|
||||
const moduleResources = new Map();
|
||||
const globalScripts = new Set();
|
||||
|
||||
const flattenTargets = (targets, parentKey = "") => {
|
||||
const flattened = {};
|
||||
|
||||
for (const [key, config] of Object.entries(targets)) {
|
||||
const fullKey = parentKey ? `${parentKey}.${key}` : key;
|
||||
flattened[fullKey] = { ...config };
|
||||
|
||||
if (
|
||||
config.childs &&
|
||||
typeof config.childs === "object" &&
|
||||
!Array.isArray(config.childs)
|
||||
) {
|
||||
const childTargets = flattenTargets(config.childs, fullKey);
|
||||
Object.assign(flattened, childTargets);
|
||||
delete flattened[fullKey].childs;
|
||||
}
|
||||
}
|
||||
|
||||
return flattened;
|
||||
};
|
||||
|
||||
const flatModules = flattenTargets(modules);
|
||||
|
||||
const createAccessProxy = (originalModules, flatModules) => {
|
||||
const createNestedProxy = (target, path = []) => {
|
||||
return new Proxy(target, {
|
||||
get(target, prop) {
|
||||
const currentPath = [...path, prop].join(".");
|
||||
|
||||
if (flatModules[currentPath]) {
|
||||
return flatModules[currentPath];
|
||||
}
|
||||
|
||||
const value = Reflect.get(target, prop);
|
||||
if (typeof value === "object" && value !== null) {
|
||||
return createNestedProxy(value, [...path, prop]);
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
set(target, prop, value) {
|
||||
const currentPath = [...path, prop].join(".");
|
||||
|
||||
if (flatModules[currentPath]) {
|
||||
flatModules[currentPath] = value;
|
||||
}
|
||||
|
||||
return Reflect.set(target, prop, value);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return createNestedProxy(originalModules);
|
||||
};
|
||||
|
||||
const accessibleModules = createAccessProxy(modules, flatModules);
|
||||
|
||||
const insertElement = (target, element, mode = "appendChild") => {
|
||||
const elementId = element.id;
|
||||
if (document.getElementById(elementId)) {
|
||||
@@ -120,12 +143,12 @@
|
||||
const observer = new MutationObserver((_mutations) => {
|
||||
if (!document.getElementById(elementId)) {
|
||||
let targetElement = document.querySelector(
|
||||
modules[moduleKey].pageSelector
|
||||
flatModules[moduleKey].pageSelector
|
||||
);
|
||||
if (
|
||||
targetElement &&
|
||||
modules[moduleKey].active &&
|
||||
modules[moduleKey].revive
|
||||
flatModules[moduleKey].active &&
|
||||
flatModules[moduleKey].revive
|
||||
) {
|
||||
if (!document.getElementById(elementId)) {
|
||||
console.log(
|
||||
@@ -145,12 +168,53 @@
|
||||
observers.set(moduleKey, observer);
|
||||
};
|
||||
|
||||
const loadResources = async (resources, type, moduleKey) => {
|
||||
if (!resources) return [];
|
||||
|
||||
const resourceArray = Array.isArray(resources) ? resources : [resources];
|
||||
const loadedResources = [];
|
||||
|
||||
for (const resource of resourceArray) {
|
||||
try {
|
||||
let element;
|
||||
if (type === "css") {
|
||||
element = document.createElement("link");
|
||||
element.rel = "stylesheet";
|
||||
element.href = `../../aura/${resource}`;
|
||||
document.head.appendChild(element);
|
||||
} else if (type === "js") {
|
||||
element = document.createElement("script");
|
||||
element.src = `../../aura/${resource}`;
|
||||
document.body.appendChild(element);
|
||||
await new Promise((resolve, reject) => {
|
||||
element.onload = resolve;
|
||||
element.onerror = reject;
|
||||
});
|
||||
}
|
||||
|
||||
if (element) {
|
||||
loadedResources.push(element);
|
||||
console.log(
|
||||
`[HugoAura / UI / ${moduleKey}] Loaded ${type}: ${resource}`
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[HugoAura / UI / Error] Failed to load ${type} ${resource} for ${moduleKey}:`,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return loadedResources;
|
||||
};
|
||||
|
||||
const loader = {
|
||||
async loadModule(moduleKey, isRevive = false) {
|
||||
if (!modules[moduleKey]?.active) return;
|
||||
if (!flatModules[moduleKey]?.active) return;
|
||||
|
||||
try {
|
||||
const config = modules[moduleKey];
|
||||
const config = flatModules[moduleKey];
|
||||
const target = await waitForElement(config.pageSelector);
|
||||
const elementId = `aura-container-${moduleKey.replace(/\./g, "-")}`;
|
||||
|
||||
@@ -169,11 +233,12 @@
|
||||
moduleResources.set(moduleKey, resources);
|
||||
|
||||
if (config.pageCSS && !isRevive) {
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
link.href = `../../aura/${config.pageCSS}`;
|
||||
document.head.appendChild(link);
|
||||
resources.add(link);
|
||||
const cssResources = await loadResources(
|
||||
config.pageCSS,
|
||||
"css",
|
||||
moduleKey
|
||||
);
|
||||
cssResources.forEach((resource) => resources.add(resource));
|
||||
}
|
||||
|
||||
const html = await fetch(`../../aura/${config.pageURI}`).then((r) =>
|
||||
@@ -185,17 +250,26 @@
|
||||
monitorParent(moduleKey, target, container, config.selectorMode);
|
||||
|
||||
if (config.pageScript && !isRevive) {
|
||||
const script = document.createElement("script");
|
||||
script.src = `../../aura/${config.pageScript}`;
|
||||
document.body.appendChild(script);
|
||||
resources.add(script);
|
||||
const jsResources = await loadResources(
|
||||
config.pageScript,
|
||||
"js",
|
||||
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) &&
|
||||
modules[moduleKey].active &&
|
||||
modules[moduleKey].revive
|
||||
flatModules[moduleKey].active &&
|
||||
flatModules[moduleKey].revive
|
||||
) {
|
||||
this.loadModule(moduleKey, true);
|
||||
}
|
||||
@@ -238,13 +312,13 @@
|
||||
|
||||
handleModuleChange(moduleKey, path = []) {
|
||||
const fullPath = [...path, moduleKey].join(".");
|
||||
if (path.length === 0 && modules[moduleKey].active) {
|
||||
if (path.length === 0 && flatModules[moduleKey].active) {
|
||||
this.loadModule(moduleKey);
|
||||
} else if (path.length === 0) {
|
||||
this.unloadModule(moduleKey);
|
||||
} else {
|
||||
if (moduleKey === "active") {
|
||||
if (modules[path[0]].active) {
|
||||
if (flatModules[path[0]].active) {
|
||||
this.loadModule(path[0]);
|
||||
} else {
|
||||
this.unloadModule(path[0]);
|
||||
@@ -297,7 +371,7 @@
|
||||
try {
|
||||
await loadGlobalJS();
|
||||
|
||||
for (const [key, config] of Object.entries(modules)) {
|
||||
for (const [key, config] of Object.entries(flatModules)) {
|
||||
if (config.active) {
|
||||
await loader.loadModule(key);
|
||||
}
|
||||
@@ -309,7 +383,7 @@
|
||||
|
||||
initialLoad();
|
||||
|
||||
return createDeepProxy(modules, (path, prop, value) => {
|
||||
return createDeepProxy(accessibleModules, (path, prop, value) => {
|
||||
loader.handleModuleChange(prop, path);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
class HooksManager {
|
||||
class RendererHooksManager {
|
||||
loadHooks() {
|
||||
if (
|
||||
global.__HUGO_AURA__.hooks &&
|
||||
Object.keys(global.__HUGO_AURA__.hooks).length !== 0
|
||||
global.__HUGO_AURA__.uiHooks &&
|
||||
Object.keys(global.__HUGO_AURA__.uiHooks).length !== 0
|
||||
) {
|
||||
return global.__HUGO_AURA__.hooks;
|
||||
return global.__HUGO_AURA__.uiHooks;
|
||||
}
|
||||
|
||||
const hooksPath = path.join(__dirname, "../../../aura/ui/hookDefinitions");
|
||||
|
||||
/** @type {import("../../types/main/core").HooksMap} */
|
||||
/** @type {import("../../types/main/core").UIHooksMap} */
|
||||
const hooks = new Map();
|
||||
|
||||
try {
|
||||
@@ -28,23 +28,23 @@ class HooksManager {
|
||||
const targetWindow = hook.windowName || path.basename(file, ".js");
|
||||
hooks.set(targetWindow, hook);
|
||||
console.log(
|
||||
`[HugoAura / Init] Loaded hook for window: ${targetWindow}`
|
||||
`[HugoAura / Init / RDH] Loaded ui hook for window: ${targetWindow}`
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[HugoAura / Init / Error] Failed to load hook ${file}:`,
|
||||
`[HugoAura / Init / RDH / Error] Failed to load ui hook ${file}:`,
|
||||
err
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Init / Error] Failed to read hooks directory:",
|
||||
"[HugoAura / Init / RDH / Error] Failed to ui hooks directory:",
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
global.__HUGO_AURA__.hooks = hooks;
|
||||
global.__HUGO_AURA__.uiHooks = hooks;
|
||||
return hooks;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class HooksManager {
|
||||
*/
|
||||
cleanupWindow(windowKey, hookedWindowProps) {
|
||||
console.log(
|
||||
`[HugoAura / Cleanup / ${windowKey}] Window destroyed, cleaning up...`
|
||||
`[HugoAura / Cleanup / RDH / ${windowKey}] Window destroyed, cleaning up...`
|
||||
);
|
||||
|
||||
if (hookedWindowProps) {
|
||||
@@ -65,6 +65,7 @@ class HooksManager {
|
||||
webContents.removeListener("destroyed", destroyedListener);
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
global.__HUGO_AURA__.hookedWindows.delete(windowKey);
|
||||
}
|
||||
|
||||
@@ -80,22 +81,25 @@ class HooksManager {
|
||||
|
||||
/** @type {import("../../types/main/core").WindowName} */
|
||||
const windowKey = `${hookConfig.windowName || windowName}`;
|
||||
// @ts-expect-error
|
||||
if (global.__HUGO_AURA__.hookedWindows.has(windowKey)) {
|
||||
console.log(
|
||||
`[HugoAura / Init] Duplicate hook for ${windowKey}, ignoring...`
|
||||
`[HugoAura / Init / RDH] Duplicate ui hook for ${windowKey}, ignoring...`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[HugoAura / Init] Hook is initializing for ${windowKey}...`);
|
||||
console.log(
|
||||
`[HugoAura / Init] Hook loaded at: ${new Date().toISOString()}`
|
||||
`[HugoAura / Init / RDH] UI Hook is initializing for ${windowKey}...`
|
||||
);
|
||||
console.log(
|
||||
`[HugoAura / Init / RDH] UI Hook loaded at: ${new Date().toISOString()}`
|
||||
);
|
||||
|
||||
const domReadyListener = () => {
|
||||
try {
|
||||
console.log(
|
||||
`[HugoAura / UI / Verb / ${windowKey}] Loading injection script...`
|
||||
`[HugoAura / RDH / Verb / ${windowKey}] Loading injection script...`
|
||||
);
|
||||
|
||||
const injectionScript = fs
|
||||
@@ -115,18 +119,18 @@ class HooksManager {
|
||||
.executeJavaScript(injectionScript, true)
|
||||
.then(() =>
|
||||
console.log(
|
||||
`[HugoAura / UI / Done / ${windowKey}] Injection script executed`
|
||||
`[HugoAura / RDH / Done / ${windowKey}] Injection script executed`
|
||||
)
|
||||
)
|
||||
.catch((err) =>
|
||||
console.error(
|
||||
`[HugoAura / UI / Error / ${windowKey}] Failed to execute injection script:`,
|
||||
`[HugoAura / RDH / Error / ${windowKey}] Failed to execute injection script:`,
|
||||
err
|
||||
)
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[HugoAura / UI / Error / ${windowKey}] Failed to load UI hook:`,
|
||||
`[HugoAura / RDH / Error / ${windowKey}] Failed to load UI hook:`,
|
||||
err
|
||||
);
|
||||
}
|
||||
@@ -135,6 +139,7 @@ class HooksManager {
|
||||
const destroyedListener = () => {
|
||||
this.cleanupWindow(
|
||||
windowKey,
|
||||
// @ts-expect-error
|
||||
global.__HUGO_AURA__.hookedWindows.get(windowKey)
|
||||
);
|
||||
};
|
||||
@@ -142,6 +147,7 @@ class HooksManager {
|
||||
webContents.on("dom-ready", domReadyListener);
|
||||
webContents.on("destroyed", destroyedListener);
|
||||
|
||||
// @ts-expect-error
|
||||
global.__HUGO_AURA__.hookedWindows.set(windowKey, {
|
||||
webContents,
|
||||
domReadyListener,
|
||||
@@ -149,9 +155,9 @@ class HooksManager {
|
||||
});
|
||||
|
||||
console.log(
|
||||
`[HugoAura / Init / Success / ${windowKey}] Hook initialized successfully!`
|
||||
`[HugoAura / Init / RDH / Success / ${windowKey}] UI Hook initialized.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HooksManager;
|
||||
module.exports = RendererHooksManager;
|
||||
@@ -6,6 +6,9 @@ const os = require("os");
|
||||
const crypto = require("crypto");
|
||||
const childProc = require("child_process");
|
||||
|
||||
const RegistryManagerClass = require("./registryManager");
|
||||
const registryManager = new RegistryManagerClass();
|
||||
|
||||
// Constants
|
||||
|
||||
const CRYPTO_SETTINGS_AES = {
|
||||
@@ -18,6 +21,7 @@ const CRYPTO_SETTINGS_AES = {
|
||||
obfuscateStr: "eCybsseK",
|
||||
hash: "sha256",
|
||||
};
|
||||
const LMAK_SETTINGS_BASE = "EncSettings\\LMAK";
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -65,19 +69,79 @@ 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 扩展名, 不然容易真被清理了 */
|
||||
this.defaultConfigPath = path.join(__dirname, "default.json");
|
||||
this.useEncConfig = false;
|
||||
this.isConfigReadFailed = false;
|
||||
this.side = "unknown";
|
||||
|
||||
if (fs.existsSync(this.encConfigPath)) {
|
||||
this.useEncConfig = true;
|
||||
} else {
|
||||
this.useEncConfig = false;
|
||||
}
|
||||
|
||||
if (global.__HUGO_AURA_EVENT_BUS__) {
|
||||
// Expect always true
|
||||
global.__HUGO_AURA_EVENT_BUS__.on(
|
||||
"$aura.config.updateConfigEncSettings",
|
||||
(/** @type {boolean} */ newVal) => {
|
||||
this.useEncConfig = newVal;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
migrateOldConfigFile() {
|
||||
if (this.configDir === path.join(os.homedir(), "Documents", "HugoAura")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldConfigPath = path.join(
|
||||
os.homedir(),
|
||||
"Documents",
|
||||
"HugoAura",
|
||||
"config.json"
|
||||
);
|
||||
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.configPath);
|
||||
return path.dirname(
|
||||
this.useEncConfig ? this.encConfigPath : this.configPath
|
||||
);
|
||||
}
|
||||
|
||||
getConfigPath() {
|
||||
return this.configPath;
|
||||
return this.useEncConfig ? this.encConfigPath : this.configPath;
|
||||
}
|
||||
|
||||
getDefaultConfig() {
|
||||
@@ -99,7 +163,7 @@ class ConfigManager {
|
||||
fs.mkdirSync(hugoAuraPath, { recursive: true });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(this.configPath)) {
|
||||
if (!fs.existsSync(this.configPath) && !fs.existsSync(this.encConfigPath)) {
|
||||
console.log("[HugoAura / Config] Creating default config file");
|
||||
const defaultConfig = this.getDefaultConfig();
|
||||
this.writeConfig(defaultConfig);
|
||||
@@ -108,11 +172,35 @@ class ConfigManager {
|
||||
|
||||
readConfig() {
|
||||
try {
|
||||
const config = JSON.parse(fs.readFileSync(this.configPath, "utf8"));
|
||||
console.log("[HugoAura / Config] Successfully loaded config:", config);
|
||||
let config = {};
|
||||
if (this.useEncConfig) {
|
||||
const hashedPasswdResultObj = this.retrieveEncPassword();
|
||||
if (hashedPasswdResultObj.success && hashedPasswdResultObj.data) {
|
||||
config = this.decryptConfig(hashedPasswdResultObj.data).data;
|
||||
|
||||
if (!config) {
|
||||
this.isConfigReadFailed = true;
|
||||
return this.getDefaultConfig(); // should be changed, too
|
||||
}
|
||||
} else {
|
||||
console.error(
|
||||
"[HugoAura / Config / ERROR] Failed to decrypt config, falling back to use plain config"
|
||||
);
|
||||
this.useEncConfig = false;
|
||||
this.isConfigReadFailed = true;
|
||||
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"));
|
||||
}
|
||||
// console.log("[HugoAura / Config] Successfully loaded config:", config);
|
||||
if (this.isConfigReadFailed) this.isConfigReadFailed = false;
|
||||
return config;
|
||||
} catch (err) {
|
||||
console.error("[HugoAura / Config] Failed to read config:", err);
|
||||
this.isConfigReadFailed = true;
|
||||
return this.getDefaultConfig();
|
||||
}
|
||||
}
|
||||
@@ -120,15 +208,40 @@ class ConfigManager {
|
||||
/**
|
||||
*
|
||||
* @param {Record<any, any>} config
|
||||
* @returns
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
writeConfig(config) {
|
||||
async writeConfig(config) {
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
this.configPath,
|
||||
JSON.stringify(config, null, 2),
|
||||
"utf8"
|
||||
);
|
||||
if (this.useEncConfig) {
|
||||
const hashedPasswdResultObj = await this.retrieveEncPasswordAsync();
|
||||
if (hashedPasswdResultObj.success && hashedPasswdResultObj.data) {
|
||||
this.encryptConfig(config, hashedPasswdResultObj.data);
|
||||
} else {
|
||||
console.error(
|
||||
"[HugoAura / Config / Write / ERROR] Failed to write config: Retrieve enc password failed"
|
||||
);
|
||||
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(
|
||||
this.configPath,
|
||||
JSON.stringify(config, null, 2),
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
|
||||
if (this.side === "renderer") {
|
||||
global.ipcRenderer.send("$aura.config.refreshMainConfig");
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("[HugoAura / Config] Failed to write config:", err);
|
||||
@@ -140,8 +253,8 @@ class ConfigManager {
|
||||
let defaultConfig = this.getDefaultConfig();
|
||||
let config = {};
|
||||
try {
|
||||
if (fs.existsSync(this.configPath)) {
|
||||
const userConfig = JSON.parse(fs.readFileSync(this.configPath, "utf8"));
|
||||
if (fs.existsSync(this.configPath) || fs.existsSync(this.encConfigPath)) {
|
||||
const userConfig = this.readConfig();
|
||||
if (global.__HUGO_AURA__.configInit) {
|
||||
config = userConfig;
|
||||
return userConfig;
|
||||
@@ -153,18 +266,340 @@ class ConfigManager {
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("[HugoAura / Config] Failed to load user config:", err);
|
||||
this.isConfigReadFailed = true;
|
||||
config = defaultConfig;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
priv_getMacAddr() {
|
||||
const netInf = os.networkInterfaces();
|
||||
const realInfs = Object.keys(netInf).filter(
|
||||
(key) =>
|
||||
!key.includes("Pseudo") &&
|
||||
!key.includes("Loopback") &&
|
||||
!key.includes("Virtual") &&
|
||||
!key.includes("Tunnel") &&
|
||||
!key.includes("Cisco") &&
|
||||
!key.includes("VPN")
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string[]} infNames
|
||||
* @param {Record<string, any>} infObj
|
||||
* @returns
|
||||
*/
|
||||
const getValidInfMac = (infNames, infObj) => {
|
||||
for (const name of infNames) {
|
||||
const target = infObj[name][0];
|
||||
const isValid = !target.internal && target.mac !== "00:00:00:00:00:00";
|
||||
if (isValid) {
|
||||
return target.mac;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const rawInfMac = getValidInfMac(realInfs, netInf);
|
||||
const macAddr = rawInfMac
|
||||
? rawInfMac.replace(/:/g, "").toUpperCase()
|
||||
: null;
|
||||
return macAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SHA256EncryptedPassword} password
|
||||
*/
|
||||
async saveEncPassword(password) {
|
||||
let macAddr = this.priv_getMacAddr();
|
||||
let fallbackToStaticKey = false;
|
||||
|
||||
if (!macAddr) {
|
||||
console.warn(
|
||||
"[HugoAura / Config / LMK] No valid network inf found, fallback to static key."
|
||||
);
|
||||
macAddr = Buffer.from(crypto.randomBytes(6))
|
||||
.toString("hex")
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
const randomSalt = crypto.randomBytes(CRYPTO_SETTINGS_AES.saltLength);
|
||||
const key = crypto.scryptSync(macAddr, randomSalt, 32);
|
||||
const iv = crypto.randomBytes(CRYPTO_SETTINGS_AES.ivLength);
|
||||
|
||||
const cipherIns = crypto.createCipheriv(CRYPTO_SETTINGS_AES.mode, key, iv, {
|
||||
// @ts-expect-error
|
||||
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
|
||||
});
|
||||
|
||||
let encryptedPassword = cipherIns.update(password, "utf-8", "hex");
|
||||
encryptedPassword += cipherIns.final("hex");
|
||||
|
||||
const authTagHex = cipherIns.getAuthTag().toString("hex");
|
||||
const ivHex = iv.toString("hex");
|
||||
const saltHex = randomSalt.toString("hex");
|
||||
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_Value",
|
||||
encryptedPassword,
|
||||
true
|
||||
);
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_IV",
|
||||
ivHex,
|
||||
true
|
||||
);
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_Salt",
|
||||
saltHex,
|
||||
true
|
||||
);
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_AuthTag",
|
||||
authTagHex,
|
||||
true
|
||||
);
|
||||
|
||||
if (fallbackToStaticKey) {
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_FakeMac",
|
||||
macAddr,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
retrieveEncPassword() {
|
||||
try {
|
||||
const authTagHex = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_AuthTag",
|
||||
true
|
||||
)?.data;
|
||||
const ivHex = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_IV",
|
||||
true
|
||||
)?.data;
|
||||
const saltHex = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_Salt",
|
||||
true
|
||||
)?.data;
|
||||
const encPasswdHex = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_Value",
|
||||
true
|
||||
)?.data;
|
||||
let isStaticKey = false;
|
||||
let macAddr = null;
|
||||
|
||||
try {
|
||||
macAddr = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_FakeMac",
|
||||
true
|
||||
)?.data;
|
||||
if (!macAddr) {
|
||||
isStaticKey = false;
|
||||
} else {
|
||||
isStaticKey = true;
|
||||
}
|
||||
} catch {
|
||||
isStaticKey = false;
|
||||
}
|
||||
|
||||
if (!isStaticKey) {
|
||||
macAddr = this.priv_getMacAddr();
|
||||
|
||||
if (!macAddr) {
|
||||
console.error(
|
||||
"[HugoAura / Config / ERROR] Failed to retrieve password from reg: MAC Address invalid."
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: new Error("Mac is null or undefined"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!saltHex || !ivHex || !authTagHex || !encPasswdHex) {
|
||||
console.error(
|
||||
"[HugoAura / Config / ERROR] Failed to retrieve password from reg: Reg keys invalid."
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: new Error("Reg key invalid"),
|
||||
};
|
||||
}
|
||||
const salt = Buffer.from(saltHex, "hex");
|
||||
const iv = Buffer.from(ivHex, "hex");
|
||||
const authTag = Buffer.from(authTagHex, "hex");
|
||||
const encPasswd = Buffer.from(encPasswdHex, "utf-8").toString();
|
||||
|
||||
const key = crypto.scryptSync(macAddr, salt, 32);
|
||||
const decipherIns = crypto.createDecipheriv(
|
||||
CRYPTO_SETTINGS_AES.mode,
|
||||
key,
|
||||
iv,
|
||||
{
|
||||
// @ts-expect-error
|
||||
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
|
||||
}
|
||||
);
|
||||
|
||||
decipherIns.setAuthTag(authTag);
|
||||
|
||||
const result = Buffer.concat([
|
||||
decipherIns.update(encPasswd, "hex"),
|
||||
decipherIns.final(),
|
||||
]).toString();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
error: null,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(
|
||||
"[HugoAura / Config / ERROR] Unexpected error occurred while retrieving password from reg, error:",
|
||||
e
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async retrieveEncPasswordAsync() {
|
||||
try {
|
||||
const authTagHex = (
|
||||
await registryManager.readRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_AuthTag",
|
||||
true
|
||||
)
|
||||
).data;
|
||||
const ivHex = (
|
||||
await registryManager.readRegKey(LMAK_SETTINGS_BASE, "LMAK_IV", true)
|
||||
).data;
|
||||
const saltHex = (
|
||||
await registryManager.readRegKey(LMAK_SETTINGS_BASE, "LMAK_Salt", true)
|
||||
).data;
|
||||
const encPasswdHex = (
|
||||
await registryManager.readRegKey(LMAK_SETTINGS_BASE, "LMAK_Value", true)
|
||||
).data;
|
||||
let isStaticKey = false;
|
||||
let macAddr = null;
|
||||
|
||||
try {
|
||||
macAddr = (
|
||||
await registryManager.readRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_FakeMac",
|
||||
true
|
||||
)
|
||||
).data;
|
||||
if (!macAddr) {
|
||||
isStaticKey = false;
|
||||
} else {
|
||||
isStaticKey = true;
|
||||
}
|
||||
} catch {
|
||||
isStaticKey = false;
|
||||
}
|
||||
|
||||
if (!isStaticKey) {
|
||||
macAddr = this.priv_getMacAddr();
|
||||
|
||||
if (!macAddr) {
|
||||
console.error(
|
||||
"[HugoAura / Config / ERROR] Failed to retrieve password from reg: MAC Address invalid."
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: new Error("Mac is null or undefined"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!saltHex || !ivHex || !authTagHex || !encPasswdHex) {
|
||||
console.error(
|
||||
"[HugoAura / Config / ERROR] Failed to retrieve password from reg: Reg keys invalid."
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: new Error("Reg key invalid"),
|
||||
};
|
||||
}
|
||||
const salt = Buffer.from(saltHex, "hex");
|
||||
const iv = Buffer.from(ivHex, "hex");
|
||||
const authTag = Buffer.from(authTagHex, "hex");
|
||||
const encPasswd = Buffer.from(encPasswdHex, "utf-8").toString();
|
||||
|
||||
const key = crypto.scryptSync(macAddr, salt, 32);
|
||||
const decipherIns = crypto.createDecipheriv(
|
||||
CRYPTO_SETTINGS_AES.mode,
|
||||
key,
|
||||
iv,
|
||||
{
|
||||
// @ts-expect-error
|
||||
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
|
||||
}
|
||||
);
|
||||
|
||||
decipherIns.setAuthTag(authTag);
|
||||
|
||||
const result = Buffer.concat([
|
||||
decipherIns.update(encPasswd, "hex"),
|
||||
decipherIns.final(),
|
||||
]).toString();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
error: null,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(
|
||||
"[HugoAura / Config / ERROR] Unexpected error occurred while retrieving password from reg, error:",
|
||||
e
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async clearEncPasswdRegKey() {
|
||||
await registryManager.delRegKey(LMAK_SETTINGS_BASE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Record<any, any>} configData
|
||||
* @param {string} passwd
|
||||
* @param {SHA256EncryptedPassword} passwd
|
||||
*/
|
||||
encryptConfig(configData, passwd) {
|
||||
registryManager.initRegistrySync();
|
||||
const salt = crypto.randomBytes(CRYPTO_SETTINGS_AES.saltLength);
|
||||
const key = crypto.pbkdf2Sync(
|
||||
passwd,
|
||||
@@ -198,15 +633,13 @@ class ConfigManager {
|
||||
|
||||
try {
|
||||
fs.writeFileSync(this.encConfigPath, base64EncConfig, "utf-8");
|
||||
// fs.rmSync(this.configPath);
|
||||
try {
|
||||
fs.unlinkSync(this.configPath);
|
||||
} catch {
|
||||
console.debug("[HugoAura / Config] Dec config not exists, skipping...");
|
||||
}
|
||||
|
||||
const _hideFileProc = childProc.spawnSync(
|
||||
"cmd.exe",
|
||||
["/c", "attrib", "+h", this.encConfigPath],
|
||||
{
|
||||
stdio: "inherit",
|
||||
}
|
||||
);
|
||||
if (!this.useEncConfig) this.useEncConfig = true;
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(
|
||||
@@ -223,95 +656,169 @@ class ConfigManager {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} passwd
|
||||
* @returns
|
||||
* @param {SHA256EncryptedPassword} passwd
|
||||
* @returns {{success: boolean, data: AuraConfig}}
|
||||
*/
|
||||
decryptConfig(passwd) {
|
||||
const FAILED_RET = {
|
||||
success: false,
|
||||
data: {},
|
||||
};
|
||||
|
||||
let base64EncConfig = null;
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(this.encConfigPath)) {
|
||||
return FAILED_RET;
|
||||
}
|
||||
base64EncConfig = fs.readFileSync(this.encConfigPath, "utf-8");
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to read encrypted config:",
|
||||
err
|
||||
);
|
||||
return FAILED_RET;
|
||||
}
|
||||
const FAILED_RET = {
|
||||
success: false,
|
||||
data: {},
|
||||
};
|
||||
|
||||
let base64EncConfig = null;
|
||||
|
||||
if (base64EncConfig) {
|
||||
const strip64EncCfg = base64EncConfig.split(
|
||||
CRYPTO_SETTINGS_AES.obfuscateStr
|
||||
)[1];
|
||||
const encryptCfg = Buffer.from(strip64EncCfg, "base64").toString("utf-8");
|
||||
/** @type {null | EncryptedConfig} */
|
||||
let parsedEncCfg = null;
|
||||
try {
|
||||
parsedEncCfg = JSON.parse(encryptCfg);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to parse encrypted config:",
|
||||
err
|
||||
);
|
||||
console.error("[HugoAura / Config] Pending data:", encryptCfg);
|
||||
}
|
||||
if (parsedEncCfg === null) return FAILED_RET;
|
||||
|
||||
const salt = Buffer.from(parsedEncCfg.salt, "base64");
|
||||
const iv = Buffer.from(parsedEncCfg.iv, "base64");
|
||||
|
||||
const authTag = Buffer.from(parsedEncCfg.authTag, "base64");
|
||||
|
||||
const key = crypto.pbkdf2Sync(
|
||||
passwd,
|
||||
salt,
|
||||
CRYPTO_SETTINGS_AES.keyIter,
|
||||
CRYPTO_SETTINGS_AES.keyLength,
|
||||
CRYPTO_SETTINGS_AES.hash
|
||||
);
|
||||
|
||||
const decipherIns = crypto.createDecipheriv(
|
||||
CRYPTO_SETTINGS_AES.mode,
|
||||
key,
|
||||
iv,
|
||||
{
|
||||
// @ts-expect-error
|
||||
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
|
||||
if (!fs.existsSync(this.encConfigPath)) {
|
||||
return FAILED_RET;
|
||||
}
|
||||
);
|
||||
|
||||
decipherIns.setAuthTag(authTag);
|
||||
|
||||
let stringifyDecCfg = Buffer.concat([
|
||||
decipherIns.update(parsedEncCfg.content, "hex"),
|
||||
decipherIns.final(),
|
||||
]).toString();
|
||||
|
||||
/** @type {null | Record<any, any>} */
|
||||
let decConfig = null;
|
||||
try {
|
||||
decConfig = JSON.parse(stringifyDecCfg);
|
||||
base64EncConfig = fs.readFileSync(this.encConfigPath, "utf-8");
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to parse decrypted config:",
|
||||
"[HugoAura / Config] Failed to read encrypted config:",
|
||||
err
|
||||
);
|
||||
console.error("[HugoAura / Config] Pending data:", decConfig);
|
||||
return FAILED_RET;
|
||||
}
|
||||
if (decConfig === null) return FAILED_RET;
|
||||
|
||||
console.debug(decConfig);
|
||||
if (base64EncConfig) {
|
||||
const strip64EncCfg = base64EncConfig.split(
|
||||
CRYPTO_SETTINGS_AES.obfuscateStr
|
||||
)[1];
|
||||
const encryptCfg = Buffer.from(strip64EncCfg, "base64").toString(
|
||||
"utf-8"
|
||||
);
|
||||
/** @type {null | EncryptedConfig} */
|
||||
let parsedEncCfg = null;
|
||||
try {
|
||||
parsedEncCfg = JSON.parse(encryptCfg);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to parse encrypted config:",
|
||||
err
|
||||
);
|
||||
console.error("[HugoAura / Config] Pending data:", encryptCfg);
|
||||
}
|
||||
if (parsedEncCfg === null) return FAILED_RET;
|
||||
|
||||
const salt = Buffer.from(parsedEncCfg.salt, "base64");
|
||||
const iv = Buffer.from(parsedEncCfg.iv, "base64");
|
||||
|
||||
const authTag = Buffer.from(parsedEncCfg.authTag, "base64");
|
||||
|
||||
const key = crypto.pbkdf2Sync(
|
||||
passwd,
|
||||
salt,
|
||||
CRYPTO_SETTINGS_AES.keyIter,
|
||||
CRYPTO_SETTINGS_AES.keyLength,
|
||||
CRYPTO_SETTINGS_AES.hash
|
||||
);
|
||||
|
||||
const decipherIns = crypto.createDecipheriv(
|
||||
CRYPTO_SETTINGS_AES.mode,
|
||||
key,
|
||||
iv,
|
||||
{
|
||||
// @ts-expect-error
|
||||
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
|
||||
}
|
||||
);
|
||||
|
||||
decipherIns.setAuthTag(authTag);
|
||||
|
||||
const stringifyDecCfg = Buffer.concat([
|
||||
decipherIns.update(parsedEncCfg.content, "hex"),
|
||||
decipherIns.final(),
|
||||
]).toString();
|
||||
|
||||
/** @type {null | Record<any, any>} */
|
||||
let decConfig = null;
|
||||
try {
|
||||
decConfig = JSON.parse(stringifyDecCfg);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to parse decrypted config:",
|
||||
err
|
||||
);
|
||||
console.error("[HugoAura / Config] Pending data:", decConfig);
|
||||
return FAILED_RET;
|
||||
}
|
||||
if (decConfig === null) return FAILED_RET;
|
||||
|
||||
// console.debug(decConfig);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: decConfig,
|
||||
};
|
||||
} else {
|
||||
console.error(
|
||||
"[HugoAura / Config] Unexpected error occurred while decrypting config: base64EncConfig is undefined"
|
||||
);
|
||||
return FAILED_RET;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Unexpected error occurred while decrypting config:",
|
||||
e
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
data: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Record<any, any> | null} curConfig
|
||||
* @param {SHA256EncryptedPassword | undefined | null} passwd
|
||||
* @returns {Promise<{success: boolean}>}
|
||||
*/
|
||||
async switchToDecConfig(curConfig, passwd = null) {
|
||||
let decConfig = null;
|
||||
if (!curConfig && passwd) {
|
||||
const getDecConfigResult = this.decryptConfig(passwd);
|
||||
if (
|
||||
!getDecConfigResult?.success ||
|
||||
!getDecConfigResult.data ||
|
||||
Object.keys(getDecConfigResult.data).length === 0
|
||||
) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to switch to decrypted config: Error decrypting config"
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
decConfig = getDecConfigResult.data;
|
||||
}
|
||||
|
||||
this.useEncConfig = false;
|
||||
await this.clearEncPasswdRegKey();
|
||||
// @ts-expect-error
|
||||
this.writeConfig(curConfig ? curConfig : decConfig);
|
||||
try {
|
||||
fs.unlinkSync(this.encConfigPath);
|
||||
} catch {
|
||||
console.debug("[HugoAura / Config] Enc config not exists, skipping...");
|
||||
}
|
||||
global.__HUGO_AURA_EVENT_BUS__.emit(
|
||||
"$aura.config.updateConfigEncSettings",
|
||||
false
|
||||
);
|
||||
if (this.side === "renderer") {
|
||||
global.ipcRenderer.invoke("$aura.config.setConfigEncSettings", {
|
||||
target: false,
|
||||
});
|
||||
global.ipcRenderer.invoke("$aura.config.dispatchConfigFromRenderer", {
|
||||
data: JSON.stringify(curConfig),
|
||||
});
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ConfigManager();
|
||||
module.exports = ConfigManager;
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
"salt": "aura"
|
||||
},
|
||||
"authModeRewrite": "default"
|
||||
},
|
||||
"vendor/screenLock": {
|
||||
"enabled": true,
|
||||
"fastfail": false,
|
||||
"showDirectUnlock": false,
|
||||
"clickBtnToExit": true,
|
||||
"disableKeyboardHook": false,
|
||||
"authRewriteType": "customActivationCode",
|
||||
"customActivationCode": {
|
||||
"activationCodeWithSalt": "cbbd87c419b1c2dbc412ae238f1f4be3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"networkRewrite": {
|
||||
@@ -16,14 +27,38 @@
|
||||
},
|
||||
"disableBehaviorAudit": {
|
||||
"enabled": true
|
||||
},
|
||||
"appearance/switchUsbInsertPromptBtn": {
|
||||
"enabled": false,
|
||||
"mode": "switch"
|
||||
},
|
||||
"appearance/banAdBlockPrompt": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"ssa": {
|
||||
"ux": {
|
||||
"easiAssistant": {
|
||||
"autoHide": false,
|
||||
"notDisplay": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"plsToken": "66ccff0d000721114514191981023333",
|
||||
"auraSettings": {
|
||||
"settingsPasswordEnabled": false,
|
||||
"settingsPasswordWithSalt": "32703D292460CC9A3B867494D6AD9A8E4A3ADF0FAA4D6867BC4D412CC3927D02E47C6D0B1763BB53E57B2241C6193433561CDA09D7C48CA03983072B876F0965",
|
||||
"encryptConfig": false,
|
||||
"appearance": {
|
||||
"enablePasswdDialogBlur": true
|
||||
"appBar": {
|
||||
"actionBtnsOnRight": false
|
||||
}
|
||||
},
|
||||
"uiAccessMethod": {
|
||||
"showEntryIcon": true,
|
||||
"fallbackAccessMethods": {
|
||||
"hotkey": false,
|
||||
"touch": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"devTools": false
|
||||
|
||||
489
src/aura/init/shared/registryManager.js
Executable file
489
src/aura/init/shared/registryManager.js
Executable file
@@ -0,0 +1,489 @@
|
||||
// @ts-check
|
||||
|
||||
const { exec, execSync } = require("child_process");
|
||||
|
||||
// Constants
|
||||
|
||||
const LOG_PREFIX = "[HugoAura / Init / Reg";
|
||||
const LOG_PREFIX_FUNC = "[HugoAura / Reg";
|
||||
const AURA_REGISTRY_PATH = [
|
||||
"HKEY_USERS",
|
||||
".DEFAULT",
|
||||
"SOFTWARE",
|
||||
"HugoAura",
|
||||
].join("\\");
|
||||
|
||||
class RegistryManager {
|
||||
/**
|
||||
* @param {string} [path]
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async handleCreateReg(path) {
|
||||
try {
|
||||
const { stdout } = await new Promise((resolve, reject) => {
|
||||
exec(
|
||||
["reg", "add", path].join(" "),
|
||||
{ encoding: "utf8" },
|
||||
(error, stdout, stderr) => {
|
||||
if (error) reject(error);
|
||||
else resolve({ stdout, stderr });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
console.log(
|
||||
`${LOG_PREFIX} / SUCCESS] Registry path ${path} successfully created.`
|
||||
);
|
||||
console.debug(`${LOG_PREFIX} / DEBUG] Reg add command stdout:`, stdout);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`${LOG_PREFIX} / ERROR] Failed creating registry path, error:`,
|
||||
e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [path]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
handleCreateRegSync(path) {
|
||||
try {
|
||||
const createResult = execSync(["reg", "add", path].join(" "), {
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
if (createResult) {
|
||||
console.log(
|
||||
`${LOG_PREFIX} / SUCCESS] Registry path ${path} successfully created.`
|
||||
);
|
||||
console.debug(
|
||||
`${LOG_PREFIX} / DEBUG] Reg add command stdout:`,
|
||||
createResult
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`${LOG_PREFIX} / ERROR] Failed creating registry path, error:`,
|
||||
e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async initRegistry() {
|
||||
try {
|
||||
const { stdout } = await new Promise((resolve, reject) => {
|
||||
exec(
|
||||
["reg", "query", AURA_REGISTRY_PATH].join(" "),
|
||||
{ encoding: "utf8" },
|
||||
(error, stdout, stderr) => {
|
||||
if (error) reject(error);
|
||||
else resolve({ stdout, stderr });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
console.log(`${LOG_PREFIX}] Registry check up success.`);
|
||||
console.debug(`${LOG_PREFIX}] Command stdout:`, stdout);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.warn(`${LOG_PREFIX} / WARN] Failed to query registry, error:`, e);
|
||||
return await this.handleCreateReg(AURA_REGISTRY_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
initRegistrySync() {
|
||||
try {
|
||||
const queryResult = execSync(
|
||||
["reg", "query", AURA_REGISTRY_PATH].join(" "),
|
||||
{ encoding: "utf8" }
|
||||
);
|
||||
|
||||
if (queryResult) {
|
||||
console.log(`${LOG_PREFIX}] Registry check up success.`);
|
||||
console.debug(`${LOG_PREFIX}] Command stdout:`, queryResult);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`${LOG_PREFIX} / WARN] Failed to query registry, error:`, e);
|
||||
return this.handleCreateRegSync(AURA_REGISTRY_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} relativePath
|
||||
* @param {string} keyName
|
||||
* @param {string} keyVal
|
||||
* @param {boolean | undefined} silent
|
||||
* @returns {Promise<{ success: boolean, error: Error | null }>}
|
||||
*/
|
||||
async createOrUpdateRegKey(relativePath, keyName, keyVal, silent = false) {
|
||||
try {
|
||||
const { stdout } = await new Promise((resolve, reject) => {
|
||||
exec(
|
||||
[
|
||||
"reg",
|
||||
"add",
|
||||
[AURA_REGISTRY_PATH, relativePath].join("\\"),
|
||||
"/v",
|
||||
keyName,
|
||||
"/t",
|
||||
"REG_SZ",
|
||||
"/d",
|
||||
`\"${keyVal}\"`,
|
||||
"/f",
|
||||
].join(" "),
|
||||
{ encoding: "utf8" },
|
||||
(error, stdout, stderr) => {
|
||||
if (error) reject(error);
|
||||
else resolve({ stdout, stderr });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (!silent) {
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully created / updated reg key ${relativePath}/${keyName} with data: ${keyVal}`
|
||||
);
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Add key command stdout:`,
|
||||
stdout
|
||||
);
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
error: null,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`${LOG_PREFIX_FUNC} / ERROR] Failed to create / update reg key, error:`,
|
||||
silent ? "<Hidden>" : e
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} relativePath
|
||||
* @param {string} keyName
|
||||
* @param {string} keyVal
|
||||
* @param {boolean | undefined} silent
|
||||
* @returns {{ success: boolean, error: Error | null }}
|
||||
*/
|
||||
createOrUpdateRegKeySync(relativePath, keyName, keyVal, silent = false) {
|
||||
try {
|
||||
const result = execSync(
|
||||
[
|
||||
"reg",
|
||||
"add",
|
||||
[AURA_REGISTRY_PATH, relativePath].join("\\"),
|
||||
"/v",
|
||||
keyName,
|
||||
"/t",
|
||||
"REG_SZ",
|
||||
"/d",
|
||||
`\"${keyVal}\"`,
|
||||
"/f",
|
||||
].join(" "),
|
||||
{ encoding: "utf8" }
|
||||
);
|
||||
|
||||
if (result) {
|
||||
if (!silent) {
|
||||
console.debug(
|
||||
`${LOG_PREFIX} / SUCCESS] Successfully created / updated reg key ${relativePath}/${keyName} with data: ${keyVal}`,
|
||||
result
|
||||
);
|
||||
console.error("");
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
error: null,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`${LOG_PREFIX_FUNC} / ERROR] Failed to create / update reg key error:`,
|
||||
e
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} relativePath
|
||||
* @param {string | null} keyName
|
||||
* @param {boolean | undefined} silent
|
||||
* @returns {Promise<{ success: boolean, error: Error | null }>}
|
||||
*/
|
||||
async delRegKey(relativePath, keyName, silent = false) {
|
||||
if (keyName === undefined) {
|
||||
throw new Error(
|
||||
`${LOG_PREFIX_FUNC} / CRITICAL] Arg \"keyName\" for function \"delRegKey\" cannot be undefined. Only null or null accepted.`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const { stdout } = await new Promise((resolve, reject) => {
|
||||
exec(
|
||||
[
|
||||
"reg",
|
||||
"delete",
|
||||
[AURA_REGISTRY_PATH, relativePath].join("\\"),
|
||||
keyName ? "/v" : "",
|
||||
keyName ? keyName : "",
|
||||
"/f",
|
||||
].join(" "),
|
||||
{ encoding: "utf8" },
|
||||
(error, stdout, stderr) => {
|
||||
if (error) reject(error);
|
||||
else resolve({ stdout, stderr });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (!silent) {
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully deleted reg key ${relativePath}/${keyName}`
|
||||
);
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Delete key command stdout:`,
|
||||
stdout
|
||||
);
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
error: null,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`${LOG_PREFIX_FUNC} / ERROR] Failed to delete reg key, error:`,
|
||||
silent ? "<Hidden>" : e
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} relativePath
|
||||
* @param {string | null} keyName
|
||||
* @param {boolean | undefined} silent
|
||||
* @returns {{ success: boolean, error: Error | null }}
|
||||
*/
|
||||
delRegKeySync(relativePath, keyName, silent = false) {
|
||||
if (keyName === undefined) {
|
||||
throw new Error(
|
||||
`${LOG_PREFIX_FUNC} / CRITICAL] Arg \"keyName\" for function \"delRegKeySync\" cannot be undefined. Only null or string accepted.`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = execSync(
|
||||
[
|
||||
"reg",
|
||||
"delete",
|
||||
[AURA_REGISTRY_PATH, relativePath].join("\\"),
|
||||
keyName ? "/v" : "",
|
||||
keyName ? keyName : "",
|
||||
"/f",
|
||||
].join(" "),
|
||||
{ encoding: "utf8" }
|
||||
);
|
||||
|
||||
if (result) {
|
||||
if (!silent) {
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully deleted reg key ${relativePath}/${keyName}`
|
||||
);
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Delete key command stdout:`,
|
||||
result
|
||||
);
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
error: null,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`${LOG_PREFIX_FUNC} / ERROR] Failed to delete reg key, error:`,
|
||||
silent ? "<Hidden>" : e
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} keyPath
|
||||
* @param {string} keyName
|
||||
* @param {boolean | undefined} silent
|
||||
* @param {boolean} absolute
|
||||
* @param {any} regex
|
||||
* @returns {Promise<{ success: boolean, data: string | null, error: Error | null }>}
|
||||
*/
|
||||
async readRegKey(
|
||||
keyPath,
|
||||
keyName,
|
||||
silent = false,
|
||||
absolute = false,
|
||||
regex = null
|
||||
) {
|
||||
try {
|
||||
const { stdout } = await new Promise((resolve, reject) => {
|
||||
exec(
|
||||
[
|
||||
"reg",
|
||||
"query",
|
||||
absolute ? keyPath : [AURA_REGISTRY_PATH, keyPath].join("\\"),
|
||||
"/v",
|
||||
`\"${keyName}\"`,
|
||||
].join(" "),
|
||||
{ encoding: "utf8" },
|
||||
(error, stdout, stderr) => {
|
||||
if (error) reject(error);
|
||||
else resolve({ stdout, stderr });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (!silent) {
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${keyPath}/${keyName}, stdout:`,
|
||||
stdout
|
||||
);
|
||||
}
|
||||
|
||||
const match = regex ? stdout.match(regex) : stdout.match(/REG_SZ\s+(.+)/);
|
||||
|
||||
if (!match) {
|
||||
console.warn(`${LOG_PREFIX} / WARN] Data not found in stdout`);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: new Error("Data not found"),
|
||||
};
|
||||
}
|
||||
|
||||
const data = match[1].trim();
|
||||
return {
|
||||
success: true,
|
||||
data,
|
||||
error: null,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(`${LOG_PREFIX} / ERROR] Failed to read reg key, error:`, e);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} keyPath
|
||||
* @param {string} keyName
|
||||
* @param {boolean | undefined} silent
|
||||
* @param {boolean} absolute
|
||||
* @param {any} regex
|
||||
* @returns {{ success: boolean, data: string | null, error: Error | null }}
|
||||
*/
|
||||
readRegKeySync(
|
||||
keyPath,
|
||||
keyName,
|
||||
silent = false,
|
||||
absolute = false,
|
||||
regex = null
|
||||
) {
|
||||
try {
|
||||
const readResult = execSync(
|
||||
[
|
||||
"reg",
|
||||
"query",
|
||||
absolute ? keyPath : [AURA_REGISTRY_PATH, keyPath].join("\\"),
|
||||
"/v",
|
||||
`\"${keyName}\"`,
|
||||
].join(" "),
|
||||
{ encoding: "utf8" }
|
||||
);
|
||||
|
||||
if (readResult) {
|
||||
if (!silent) {
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${keyPath}/${keyName}, stdout:`,
|
||||
readResult
|
||||
);
|
||||
}
|
||||
const match = regex
|
||||
? readResult.match(regex)
|
||||
: readResult.match(/REG_SZ\s+(.+)/);
|
||||
|
||||
if (!match) {
|
||||
console.warn(`${LOG_PREFIX} / WARN] Data not found in stdout`);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: new Error("Data not found"),
|
||||
};
|
||||
}
|
||||
|
||||
const data = match[1].trim();
|
||||
return {
|
||||
success: true,
|
||||
data,
|
||||
error: null,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`${LOG_PREFIX} / ERROR] Failed to read reg key, error:`,
|
||||
silent ? "<Hidden>" : e
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RegistryManager;
|
||||
@@ -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) {
|
||||
|
||||
20
src/aura/jsRewrite/network/appearance/banAdBlockPrompt.js
Normal file
20
src/aura/jsRewrite/network/appearance/banAdBlockPrompt.js
Normal 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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
10
src/aura/jsRewrite/vendor/passwordValidation.js
vendored
10
src/aura/jsRewrite/vendor/passwordValidation.js
vendored
@@ -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;
|
||||
}
|
||||
|
||||
1403
src/aura/jsRewrite/vendor/screenLock.js
vendored
Executable file
1403
src/aura/jsRewrite/vendor/screenLock.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
17
src/aura/mainProcess/hooks/screenLock.js
Executable file
17
src/aura/mainProcess/hooks/screenLock.js
Executable file
@@ -0,0 +1,17 @@
|
||||
const hookFn = (central, appIns, browserWindowIns) => {
|
||||
const __config = global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"];
|
||||
|
||||
const removeKeyboardHook = () => {
|
||||
const { dllForHookBoard } = central(29);
|
||||
|
||||
setTimeout(() => {
|
||||
dllForHookBoard.UnHookKeyBoard();
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
if (__config.disableKeyboardHook) {
|
||||
removeKeyboardHook();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { hookFunc: hookFn };
|
||||
11
src/aura/types/main/core.d.ts
vendored
11
src/aura/types/main/core.d.ts
vendored
@@ -21,13 +21,6 @@ type HookedWindowsMap = Map<WindowName, HookedWindow>;
|
||||
|
||||
type HookRequire = any;
|
||||
|
||||
type HooksMap = Map<WindowName, HookRequire>;
|
||||
type UIHooksMap = Map<WindowName, HookRequire>;
|
||||
|
||||
interface MainProcessGlobal {
|
||||
hookedWindows: HookedWindowsMap;
|
||||
hooks: HooksMap;
|
||||
configInit: boolean;
|
||||
plsStats: PLSStatus | null;
|
||||
plsSettings: Record<any, any> | null;
|
||||
plsRules: Record<any, any> | null;
|
||||
}
|
||||
type WindowHooksMap = Map<WindowName, HookRequire>;
|
||||
|
||||
21
src/aura/types/main/ipc/fs.d.ts
vendored
Executable file
21
src/aura/types/main/ipc/fs.d.ts
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
type DownloadTaskID = string;
|
||||
type DownloadTaskStatus = "waiting" | "progressing" | "done" | "failed" | "cancelled" | "struggling";
|
||||
|
||||
interface DownloadTask {
|
||||
id: DownloadTaskID;
|
||||
progress: float;
|
||||
curBytes?: number;
|
||||
totalBytes?: number;
|
||||
status: DownloadTaskStatus;
|
||||
dlUrl: string | null;
|
||||
savePath: string | null;
|
||||
message?: string;
|
||||
errorObj?: Error;
|
||||
}
|
||||
|
||||
interface FSTasks {
|
||||
downloadTasks: Map<
|
||||
DownloadTaskID,
|
||||
{ status: DownloadTaskStatus; cancelReq: any }
|
||||
>;
|
||||
}
|
||||
9
src/aura/types/render/global.d.ts
vendored
9
src/aura/types/render/global.d.ts
vendored
@@ -3,12 +3,15 @@ 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>;
|
||||
type UIReactivesObject = Record<string, any>;
|
||||
|
||||
15
src/aura/types/render/uiHook.d.ts
vendored
15
src/aura/types/render/uiHook.d.ts
vendored
@@ -1,12 +1,13 @@
|
||||
import { WindowName } from "../main/core";
|
||||
|
||||
interface UIHookTarget {
|
||||
active: boolean;
|
||||
pageURI: string;
|
||||
pageScript: string;
|
||||
pageSelector: string;
|
||||
selectorMode: "insertAfter" | "insertBefore" | "appendChild";
|
||||
pageCSS: string;
|
||||
active?: boolean;
|
||||
pageURI?: string;
|
||||
pageScript?: string;
|
||||
pageSelector?: string;
|
||||
selectorMode?: "insertAfter" | "insertBefore" | "appendChild";
|
||||
pageCSS?: string;
|
||||
childs?: Record<AuraElementUID, UIHookTarget>;
|
||||
revive?: boolean;
|
||||
}
|
||||
|
||||
@@ -23,3 +24,5 @@ interface UIHookConfig {
|
||||
interface UIHookConfigFin extends UIHookConfig {
|
||||
windowName: WindowName;
|
||||
}
|
||||
|
||||
type UIHooksObject = Record<AuraElementUID, UIHookConfigFin>;
|
||||
|
||||
29
src/aura/types/shared/aikari/status.d.ts
vendored
Normal file
29
src/aura/types/shared/aikari/status.d.ts
vendored
Normal 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";
|
||||
7
src/aura/types/shared/pls/websocket.d.ts → src/aura/types/shared/aikari/websocket.d.ts
vendored
Executable file → Normal file
7
src/aura/types/shared/pls/websocket.d.ts → src/aura/types/shared/aikari/websocket.d.ts
vendored
Executable file → Normal 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>;
|
||||
3
src/aura/types/shared/config.d.ts
vendored
3
src/aura/types/shared/config.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
type AES256EncryptedConfig = string;
|
||||
type Base64String = string;
|
||||
type SHA256EncryptedPassword = string;
|
||||
|
||||
interface EncryptedConfig {
|
||||
content: AES256EncryptedConfig;
|
||||
@@ -7,3 +8,5 @@ interface EncryptedConfig {
|
||||
salt: Base64String;
|
||||
iv: Base64String;
|
||||
}
|
||||
|
||||
type AuraConfig = Record<any, any>;
|
||||
|
||||
49
src/aura/types/shared/global.d.ts
vendored
Executable file
49
src/aura/types/shared/global.d.ts
vendored
Executable file
@@ -0,0 +1,49 @@
|
||||
import { IpcRenderer } from "electron";
|
||||
import type EventBus from "../../utils/eventBus";
|
||||
import { HookedWindowsMap, UIHooksMap, WindowHooksMap } from "../main/core";
|
||||
import { UIHooksObject } from "../render/uiHook";
|
||||
import ConfigManager from "../../init/shared/configManager";
|
||||
import { AikariStatus } from "./aikari/status";
|
||||
|
||||
type MainProcessOnlyVal<T> = T;
|
||||
type RendererProcessOnlyVal<T> = T;
|
||||
|
||||
interface GlobalHugoAuraInfo {
|
||||
central?: MainProcessOnlyVal<(...args: any) => any>;
|
||||
configInit: boolean;
|
||||
fsTasks?: MainProcessOnlyVal<FSTasks>;
|
||||
hookedWindows?: MainProcessOnlyVal<HookedWindowsMap>;
|
||||
ipcInit?: MainProcessOnlyVal<boolean>;
|
||||
auraDir: MainProcessOnlyVal<string>;
|
||||
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 {
|
||||
domains: string[];
|
||||
aikariUpdate: string;
|
||||
auraUpdate: string;
|
||||
}
|
||||
|
||||
type GlobalHugoAuraConfig = AuraConfig;
|
||||
|
||||
declare global {
|
||||
var ipcRenderer: RendererProcessOnlyVal<IpcRenderer>;
|
||||
var _ACCEPT_DATA: any;
|
||||
var __HUGO_AURA__: GlobalHugoAuraInfo;
|
||||
var __HUGO_AURA_API__: GlobalHugoAuraApiInfo;
|
||||
var __HUGO_AURA_CONFIG__: GlobalHugoAuraConfig;
|
||||
var __HUGO_AURA_CONFIG_MGR__: ConfigManager;
|
||||
var __HUGO_AURA_EVENT_BUS__: EventBus;
|
||||
var __HUGO_AURA_DEBUG__: RendererProcessOnlyVal<Record<any, any>>;
|
||||
var __HUGO_AURA_GLOBAL__: RendererProcessOnlyVal<Record<any, any>>;
|
||||
var __HUGO_AURA_HOOK__: RendererProcessOnlyVal<Record<any, any>>;
|
||||
var __HUGO_AURA_LOADER__: RendererProcessOnlyVal<UIHooksObject>;
|
||||
var __HUGO_AURA_UI_FUNCTIONS__: RendererProcessOnlyVal<UIFunctionsObject>;
|
||||
var __HUGO_AURA_UI_REACTIVES__: RendererProcessOnlyVal<UIReactivesObject>;
|
||||
}
|
||||
9
src/aura/types/shared/pls/status.d.ts
vendored
9
src/aura/types/shared/pls/status.d.ts
vendored
@@ -1,9 +0,0 @@
|
||||
interface PLSStatus {
|
||||
installed: boolean;
|
||||
detached: boolean;
|
||||
connected: boolean;
|
||||
launched: boolean;
|
||||
status: string;
|
||||
version: string;
|
||||
authToken: string;
|
||||
}
|
||||
129
src/aura/ui/aikari/onConnectedSeq.js
Normal file
129
src/aura/ui/aikari/onConnectedSeq.js
Normal 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 };
|
||||
2
src/aura/ui/pls/pushHandler.js → src/aura/ui/aikari/pushHandler.js
Executable file → Normal file
2
src/aura/ui/pls/pushHandler.js → src/aura/ui/aikari/pushHandler.js
Executable file → Normal file
@@ -7,7 +7,7 @@ const { configRouteHandler } = require(`${REQUIRE_BASE}/routes/config`);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PLSPush} parsedWsMsg
|
||||
* @param {AikariPush} parsedWsMsg
|
||||
* @returns
|
||||
*/
|
||||
const pushMsgHandler = (parsedWsMsg) => {
|
||||
49
src/aura/ui/aikari/routes/basic.js
Normal file
49
src/aura/ui/aikari/routes/basic.js
Normal 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 };
|
||||
8
src/aura/ui/pls/routes/config.js → src/aura/ui/aikari/routes/config.js
Executable file → Normal file
8
src/aura/ui/pls/routes/config.js → src/aura/ui/aikari/routes/config.js
Executable file → Normal 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;
|
||||
177
src/aura/ui/composables/aikariConfigManager.js
Normal file
177
src/aura/ui/composables/aikariConfigManager.js
Normal 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,
|
||||
};
|
||||
@@ -1,93 +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_GLOBAL__.plsStatus = plsStatus;
|
||||
return plsStatus;
|
||||
};
|
||||
|
||||
const updatePlsSettingsFromLocal = async () => {
|
||||
const plsSettings = (
|
||||
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsSettings`)
|
||||
).data;
|
||||
global.__HUGO_AURA_GLOBAL__.plsSettings = plsSettings;
|
||||
return plsSettings;
|
||||
};
|
||||
|
||||
const updatePlsRulesFromLocal = async () => {
|
||||
const plsRules = (
|
||||
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsRules`)
|
||||
).data;
|
||||
global.__HUGO_AURA_GLOBAL__.plsRules = plsRules;
|
||||
return plsRules;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
const genRandomHex = () => {
|
||||
let result = "";
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const randomNum = Math.floor(Math.random() * 0x10000);
|
||||
result += randomNum.toString(16).padStart(4, "0");
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} configKey
|
||||
* @param {any} configValue
|
||||
*/
|
||||
const updatePlsConfigToRemote = async (configKey, configValue) => {
|
||||
const configLevels = configKey.split(".");
|
||||
/** @type {Record<any, any>} */
|
||||
let localUpdateTarget =
|
||||
configLevels[0] === "ruleSettings"
|
||||
? global.__HUGO_AURA_GLOBAL__.plsRules
|
||||
: global.__HUGO_AURA_GLOBAL__.plsSettings;
|
||||
for (const level of configLevels.slice(0, -1)) {
|
||||
localUpdateTarget = localUpdateTarget[level];
|
||||
}
|
||||
localUpdateTarget[configLevels.slice(-1)[0]] = configValue;
|
||||
|
||||
const plsConfigUpdateEvent = new CustomEvent("onPLSConfigUpdate", {
|
||||
detail: {
|
||||
path: configKey,
|
||||
value: configValue,
|
||||
},
|
||||
});
|
||||
document.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_GLOBAL__.plsSettings,
|
||||
rules: global.__HUGO_AURA_GLOBAL__.plsRules,
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
updatePlsRulesFromLocal,
|
||||
updatePlsStatusFromLocal,
|
||||
updatePlsSettingsFromLocal,
|
||||
updatePlsConfigToRemote,
|
||||
};
|
||||
34
src/aura/ui/composables/rawCmdExec/app.js
Normal file
34
src/aura/ui/composables/rawCmdExec/app.js
Normal 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;
|
||||
28
src/aura/ui/composables/rawCmdExec/fs.js
Normal file
28
src/aura/ui/composables/rawCmdExec/fs.js
Normal 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;
|
||||
@@ -18,14 +18,14 @@ 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_GLOBAL__.plsStatus.detached) {
|
||||
const relaunchBtn = document.getElementById("plsRelaunchBtn");
|
||||
if (global.__HUGO_AURA__.aikariStats.detached) {
|
||||
const relaunchBtn = document.getElementById("aikariRelaunchBtn");
|
||||
relaunchBtn.disabled = true;
|
||||
relaunchBtn.textContent = "分离模式下无法执行"
|
||||
relaunchBtn.textContent = "分离模式下无法执行";
|
||||
}
|
||||
|
||||
if (!toastBs.isShown()) toastBs.show();
|
||||
@@ -36,12 +36,456 @@ const showToast = (entry) => {
|
||||
showReloadToast();
|
||||
} else if (entry.restart) {
|
||||
showRelaunchToast();
|
||||
} else if (entry.restartPLS) {
|
||||
showRelaunchPLSToast();
|
||||
}
|
||||
};
|
||||
|
||||
const settingsRenderer = (pendingEl, settingsObj, isPls = false) => {
|
||||
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) => {
|
||||
if (Array.isArray(child)) {
|
||||
for (const perEl of child) {
|
||||
isInsert ? parent.appendChild(perEl) : parent.removeChild(perEl);
|
||||
}
|
||||
} else {
|
||||
isInsert ? parent.appendChild(child) : parent.removeChild(child);
|
||||
}
|
||||
};
|
||||
|
||||
const createOnLeaveEvtListener =
|
||||
global.__HUGO_AURA_GLOBAL__.utils.createOnLeaveEvtListener;
|
||||
|
||||
const renderInputArea = (entry, operationArea, descriptionArea) => {
|
||||
switch (entry.type) {
|
||||
case "switch": {
|
||||
const switchEl = document.createElement("input");
|
||||
switchEl.classList.add("form-check-input");
|
||||
switchEl.type = "checkbox";
|
||||
switchEl.role = "switch";
|
||||
switchEl.id = entry.id;
|
||||
const elValue = entry.valueGetter();
|
||||
switchEl.value = elValue;
|
||||
switchEl.checked = elValue;
|
||||
switchEl.addEventListener("change", async (event) => {
|
||||
showToast(entry);
|
||||
await entry.callbackFn(
|
||||
event.target.checked,
|
||||
switchEl,
|
||||
operationArea,
|
||||
descriptionArea
|
||||
);
|
||||
});
|
||||
operationArea.classList.add("form-check", "form-switch");
|
||||
return switchEl;
|
||||
}
|
||||
case "radio": {
|
||||
const elValue = entry.valueGetter();
|
||||
const elArr = [];
|
||||
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");
|
||||
radioEl.value = template;
|
||||
radioEl.classList.add("form-check-input");
|
||||
radioEl.type = "radio";
|
||||
radioEl.name = `${entry.id}Radios`;
|
||||
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,
|
||||
radioEl,
|
||||
operationArea,
|
||||
descriptionArea
|
||||
);
|
||||
}
|
||||
});
|
||||
inlineContainerEl.appendChild(radioEl);
|
||||
const labelEl = document.createElement("label");
|
||||
labelEl.classList.add("form-check-label");
|
||||
labelEl.setAttribute("for", radioEl.id);
|
||||
labelEl.textContent =
|
||||
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);
|
||||
}
|
||||
return elArr;
|
||||
}
|
||||
case "input": {
|
||||
const inputEl = document.createElement("input");
|
||||
inputEl.classList.add("form-control");
|
||||
inputEl.type = entry.subType;
|
||||
inputEl.value = entry.valueGetter();
|
||||
inputEl.placeholder = entry.placeHolder;
|
||||
inputEl.id = entry.id;
|
||||
|
||||
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);
|
||||
|
||||
if (inputEl.className.includes("is-invalid")) {
|
||||
inputEl.classList.remove("is-invalid");
|
||||
descriptionArea.textContent = entry.description;
|
||||
descriptionArea.classList.remove("ase-desc-error-hint");
|
||||
}
|
||||
} else {
|
||||
inputEl.classList.add("is-invalid");
|
||||
descriptionArea.textContent = result.hint;
|
||||
descriptionArea.classList.add("ase-desc-error-hint");
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
const renderNormalSettingsItem = (entry, formEl) => {
|
||||
const entryContainerEl = document.createElement("div");
|
||||
entryContainerEl.classList.add("aura-settings-entry");
|
||||
entryContainerEl.id = `${entry.id}Container`;
|
||||
|
||||
const entryInfoContainerEl = document.createElement("div");
|
||||
entryInfoContainerEl.classList.add("aura-settings-entry-info-container");
|
||||
const entryTitle = document.createElement("p");
|
||||
entryTitle.classList.add("aura-settings-entry-title");
|
||||
entryTitle.textContent = entry.name;
|
||||
if (entry.restart) {
|
||||
const powerIcon = document.createElement("i");
|
||||
powerIcon.classList.add(
|
||||
"layui-icon",
|
||||
"layui-icon-logout",
|
||||
"aura-settings-entry-property-icon"
|
||||
);
|
||||
powerIcon.setAttribute("data-bs-toggle", "tooltip");
|
||||
powerIcon.setAttribute("data-bs-placement", "top");
|
||||
powerIcon.setAttribute("data-bs-title", "需要重启 Electron 进程");
|
||||
entryTitle.appendChild(powerIcon);
|
||||
}
|
||||
if (entry.aikariRequired) {
|
||||
const aikariIcon = document.createElement("i");
|
||||
aikariIcon.classList.add(
|
||||
"layui-icon",
|
||||
"layui-icon-component",
|
||||
"aura-settings-entry-property-icon"
|
||||
);
|
||||
aikariIcon.setAttribute("data-bs-toggle", "tooltip");
|
||||
aikariIcon.setAttribute("data-bs-placement", "top");
|
||||
aikariIcon.setAttribute("data-bs-title", "需要 Aikari 支持");
|
||||
entryTitle.appendChild(aikariIcon);
|
||||
}
|
||||
if (entry.restartAikari) {
|
||||
const aikariIcon = document.createElement("i");
|
||||
aikariIcon.classList.add(
|
||||
"layui-icon",
|
||||
"layui-icon-logout",
|
||||
"aura-settings-entry-property-icon"
|
||||
);
|
||||
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");
|
||||
reloadIcon.classList.add(
|
||||
"layui-icon",
|
||||
"layui-icon-refresh",
|
||||
"aura-settings-entry-property-icon"
|
||||
);
|
||||
reloadIcon.setAttribute("data-bs-toggle", "tooltip");
|
||||
reloadIcon.setAttribute("data-bs-placement", "top");
|
||||
reloadIcon.setAttribute("data-bs-title", "需要重载页面");
|
||||
entryTitle.appendChild(reloadIcon);
|
||||
}
|
||||
|
||||
const createToolTipIcon = (type, content) => {
|
||||
const tipIcon = document.createElement("i");
|
||||
tipIcon.classList.add(
|
||||
"layui-icon",
|
||||
"layui-icon-tips",
|
||||
"aura-settings-entry-property-icon"
|
||||
);
|
||||
if (type === "warning") {
|
||||
tipIcon.classList.add("aura-settings-entry-warning-icon");
|
||||
}
|
||||
tipIcon.setAttribute("data-bs-toggle", "tooltip");
|
||||
tipIcon.setAttribute("data-bs-placement", "top");
|
||||
tipIcon.setAttribute("data-bs-title", content);
|
||||
entryTitle.appendChild(tipIcon);
|
||||
};
|
||||
|
||||
if (entry.tip) {
|
||||
createToolTipIcon("tip", entry.tipTitle);
|
||||
}
|
||||
|
||||
if (entry.warning) {
|
||||
createToolTipIcon("warning", entry.warningContent);
|
||||
}
|
||||
|
||||
const entryDescription = document.createElement("p");
|
||||
entryDescription.classList.add("aura-settings-entry-desc");
|
||||
entryDescription.innerHTML = entry.description;
|
||||
entryInfoContainerEl.appendChild(entryTitle);
|
||||
entryInfoContainerEl.appendChild(entryDescription);
|
||||
|
||||
entryContainerEl.appendChild(entryInfoContainerEl);
|
||||
|
||||
const entryOperationArea = document.createElement("div");
|
||||
entryOperationArea.classList.add("aura-settings-entry-operation-area");
|
||||
|
||||
let targetEl = renderInputArea(entry, entryOperationArea, entryDescription);
|
||||
insertOrRemoveEl(entryOperationArea, targetEl, true);
|
||||
|
||||
if (entry.reactive) {
|
||||
const evtListener = (event) => {
|
||||
if (entry.reactiveVal.includes(event.detail.path.join("."))) {
|
||||
insertOrRemoveEl(entryOperationArea, targetEl, false);
|
||||
targetEl = renderInputArea(entry, entryOperationArea, entryDescription);
|
||||
insertOrRemoveEl(entryOperationArea, targetEl, true);
|
||||
}
|
||||
};
|
||||
const channel = entry.aikariRequired
|
||||
? "onAikariConfigUpdate"
|
||||
: "onHugoAuraConfigUpdate";
|
||||
entryContainerEl.addEventListener(channel, evtListener);
|
||||
// createOnLeaveEvtListener(channel, evtListener);
|
||||
}
|
||||
|
||||
const updateDisableStatus = (entry) => {
|
||||
const isDisabledRet = entry.auraDisable();
|
||||
setDisableStatus(
|
||||
entryOperationArea,
|
||||
isDisabledRet.value,
|
||||
isDisabledRet.tooltip
|
||||
);
|
||||
};
|
||||
|
||||
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) {
|
||||
try {
|
||||
updateDisableStatus(entry);
|
||||
} catch {
|
||||
setDisableStatus(entryOperationArea, false);
|
||||
}
|
||||
} else {
|
||||
if (!entry.alwaysEnable) {
|
||||
setDisableStatus(entryOperationArea, true, "连接至 Aikari 以继续");
|
||||
}
|
||||
}
|
||||
};
|
||||
entryContainerEl.addEventListener("onAikariStatsUpdate", evtListener);
|
||||
// createOnLeaveEvtListener("onAikariStatsUpdate", evtListener);
|
||||
}
|
||||
entryContainerEl.appendChild(entryOperationArea);
|
||||
const isShow = entry.auraIf();
|
||||
if (!isShow) entryContainerEl.classList.add("aura-settings-entry-hidden");
|
||||
|
||||
if (entry.associateVal) {
|
||||
const evtListener = (event) => {
|
||||
if (!entry.associateVal.includes(event.detail.path.join("."))) return;
|
||||
const cls = entryContainerEl.classList;
|
||||
const isShow = entry.auraIf();
|
||||
isShow
|
||||
? cls.remove("aura-settings-entry-hidden")
|
||||
: cls.add("aura-settings-entry-hidden");
|
||||
|
||||
if (entry.auraDisable && !entry.alwaysEnable) {
|
||||
updateDisableStatus(entry);
|
||||
}
|
||||
};
|
||||
const channel = entry.aikariRequired
|
||||
? "onAikariConfigUpdate"
|
||||
: "onHugoAuraConfigUpdate";
|
||||
entryContainerEl.addEventListener(channel, evtListener);
|
||||
// createOnLeaveEvtListener(channel, evtListener);
|
||||
}
|
||||
|
||||
formEl.appendChild(entryContainerEl);
|
||||
};
|
||||
|
||||
const renderPreviewItem = (entry, formEl) => {
|
||||
const elementId = entry.customId ? entry.customId : `${entry.id}Container`;
|
||||
const eventChannel = entry.listenerType;
|
||||
|
||||
const separateHrContainer = document.createElement("div");
|
||||
separateHrContainer.classList.add("aura-settings-preview-area-hr-container");
|
||||
|
||||
const hrTitle = document.createElement("p");
|
||||
hrTitle.textContent = "预览";
|
||||
|
||||
const hrElement = document.createElement("hr");
|
||||
hrElement.classList.add("aura-settings-preview-hr");
|
||||
|
||||
separateHrContainer.appendChild(hrElement);
|
||||
separateHrContainer.appendChild(hrTitle);
|
||||
separateHrContainer.appendChild(hrElement.cloneNode());
|
||||
|
||||
formEl.appendChild(separateHrContainer);
|
||||
|
||||
const previewContainerEl = document.createElement("div");
|
||||
previewContainerEl.classList.add("aura-settings-preview-area-container");
|
||||
previewContainerEl.id = elementId;
|
||||
|
||||
const eventListener = (event) => {
|
||||
const childs = previewContainerEl.querySelectorAll("*");
|
||||
Array.from(childs).forEach((el) => {
|
||||
el.dispatchEvent(
|
||||
new CustomEvent("onAssociateValueUpdated", { detail: event.detail })
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener(
|
||||
eventChannel === "aikari"
|
||||
? "onAikariConfigUpdate"
|
||||
: "onHugoAuraConfigUpdate",
|
||||
eventListener
|
||||
);
|
||||
createOnLeaveEvtListener(eventListener); // Clean up
|
||||
|
||||
formEl.appendChild(previewContainerEl);
|
||||
|
||||
setTimeout(() => {
|
||||
global.__HUGO_AURA_LOADER__[entry.loaderTarget].active = true;
|
||||
}, 50);
|
||||
};
|
||||
|
||||
const renderChild = (entry, formEl) => {
|
||||
switch (entry.type) {
|
||||
case "preview":
|
||||
renderPreviewItem(entry, formEl);
|
||||
break;
|
||||
default:
|
||||
renderNormalSettingsItem(entry, formEl);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const settingsRenderer = (pendingEl, settingsObj) => {
|
||||
const formEl = document.createElement("form");
|
||||
formEl.classList.add("aura-settings-form");
|
||||
for (const category of settingsObj) {
|
||||
@@ -50,180 +494,7 @@ const settingsRenderer = (pendingEl, settingsObj, isPls = false) => {
|
||||
categoryTitleEl.textContent = category.categoryName;
|
||||
formEl.appendChild(categoryTitleEl);
|
||||
for (const entry of category.child) {
|
||||
const entryContainerEl = document.createElement("div");
|
||||
entryContainerEl.classList.add("aura-settings-entry");
|
||||
entryContainerEl.id = `${entry.id}Container`;
|
||||
|
||||
const entryInfoContainerEl = document.createElement("div");
|
||||
entryInfoContainerEl.classList.add("aura-settings-entry-info-container");
|
||||
const entryTitle = document.createElement("p");
|
||||
entryTitle.classList.add("aura-settings-entry-title");
|
||||
entryTitle.textContent = entry.name;
|
||||
if (entry.restart) {
|
||||
const powerIcon = document.createElement("i");
|
||||
powerIcon.classList.add(
|
||||
"layui-icon",
|
||||
"layui-icon-logout",
|
||||
"aura-settings-entry-property-icon"
|
||||
);
|
||||
powerIcon.setAttribute("data-bs-toggle", "tooltip");
|
||||
powerIcon.setAttribute("data-bs-placement", "top");
|
||||
powerIcon.setAttribute("data-bs-title", "需要重启 Electron 进程");
|
||||
entryTitle.appendChild(powerIcon);
|
||||
}
|
||||
if (entry.restartPLS) {
|
||||
const plsIcon = document.createElement("i");
|
||||
plsIcon.classList.add(
|
||||
"layui-icon",
|
||||
"layui-icon-logout",
|
||||
"aura-settings-entry-property-icon"
|
||||
);
|
||||
plsIcon.setAttribute("data-bs-toggle", "tooltip");
|
||||
plsIcon.setAttribute("data-bs-placement", "top");
|
||||
plsIcon.setAttribute("data-bs-title", "需要重启 PLS 进程");
|
||||
entryTitle.appendChild(plsIcon);
|
||||
}
|
||||
if (entry.reload) {
|
||||
const reloadIcon = document.createElement("i");
|
||||
reloadIcon.classList.add(
|
||||
"layui-icon",
|
||||
"layui-icon-refresh",
|
||||
"aura-settings-entry-property-icon"
|
||||
);
|
||||
reloadIcon.setAttribute("data-bs-toggle", "tooltip");
|
||||
reloadIcon.setAttribute("data-bs-placement", "top");
|
||||
reloadIcon.setAttribute("data-bs-title", "需要重载页面");
|
||||
entryTitle.appendChild(reloadIcon);
|
||||
}
|
||||
if (entry.tip) {
|
||||
const tipIcon = document.createElement("i");
|
||||
tipIcon.classList.add(
|
||||
"layui-icon",
|
||||
"layui-icon-tips",
|
||||
"aura-settings-entry-property-icon"
|
||||
);
|
||||
tipIcon.setAttribute("data-bs-toggle", "tooltip");
|
||||
tipIcon.setAttribute("data-bs-placement", "top");
|
||||
tipIcon.setAttribute("data-bs-title", entry.tipTitle);
|
||||
entryTitle.appendChild(tipIcon);
|
||||
}
|
||||
const entryDescription = document.createElement("p");
|
||||
entryDescription.classList.add("aura-settings-entry-desc");
|
||||
entryDescription.textContent = entry.description;
|
||||
entryInfoContainerEl.appendChild(entryTitle);
|
||||
entryInfoContainerEl.appendChild(entryDescription);
|
||||
|
||||
entryContainerEl.appendChild(entryInfoContainerEl);
|
||||
|
||||
const entryOperationArea = document.createElement("div");
|
||||
entryOperationArea.classList.add("aura-settings-entry-operation-area");
|
||||
switch (entry.type) {
|
||||
case "switch":
|
||||
{
|
||||
const switchEl = document.createElement("input");
|
||||
switchEl.classList.add("form-check-input");
|
||||
switchEl.type = "checkbox";
|
||||
switchEl.role = "switch";
|
||||
switchEl.id = entry.id;
|
||||
const elValue = entry.valueGetter();
|
||||
switchEl.value = elValue;
|
||||
switchEl.checked = elValue;
|
||||
switchEl.addEventListener("change", (event) => {
|
||||
showToast(entry);
|
||||
entry.callbackFn(event.target.checked);
|
||||
});
|
||||
entryOperationArea.classList.add("form-check", "form-switch");
|
||||
entryOperationArea.appendChild(switchEl);
|
||||
}
|
||||
break;
|
||||
case "radio":
|
||||
{
|
||||
const elValue = entry.valueGetter();
|
||||
for (const template of entry.templates) {
|
||||
const inlineContainerEl = document.createElement("div");
|
||||
inlineContainerEl.classList.add(
|
||||
"form-check",
|
||||
"form-check-inline"
|
||||
);
|
||||
const radioEl = document.createElement("input");
|
||||
radioEl.value = template;
|
||||
radioEl.classList.add("form-check-input");
|
||||
radioEl.type = "radio";
|
||||
radioEl.name = `${entry.id}Radios`;
|
||||
radioEl.id = `${entry.id}Radio${entry.templates.indexOf(
|
||||
template
|
||||
)}`;
|
||||
radioEl.checked = template === elValue ? true : false;
|
||||
radioEl.addEventListener("change", (event) => {
|
||||
if (event.target.checked) {
|
||||
showToast(entry);
|
||||
entry.callbackFn(event.target.value);
|
||||
}
|
||||
});
|
||||
inlineContainerEl.appendChild(radioEl);
|
||||
const labelEl = document.createElement("label");
|
||||
labelEl.classList.add("form-check-label");
|
||||
labelEl.setAttribute("for", radioEl.id);
|
||||
labelEl.textContent =
|
||||
entry.templateLabels[entry.templates.indexOf(template)];
|
||||
inlineContainerEl.appendChild(labelEl);
|
||||
entryOperationArea.appendChild(inlineContainerEl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "input":
|
||||
{
|
||||
const inputEl = document.createElement("input");
|
||||
inputEl.classList.add("form-control");
|
||||
inputEl.type = entry.subType;
|
||||
inputEl.value = entry.valueGetter();
|
||||
inputEl.placeholder = entry.placeHolder;
|
||||
inputEl.id = entry.id;
|
||||
inputEl.addEventListener("change", (event) => {
|
||||
const result = entry.callbackFn(event.target.value);
|
||||
const success = result.valid;
|
||||
if (success) {
|
||||
showToast(entry);
|
||||
|
||||
if (inputEl.className.includes("is-invalid")) {
|
||||
inputEl.classList.remove("is-invalid");
|
||||
entryDescription.textContent = entry.description;
|
||||
entryDescription.classList.remove("ase-desc-error-hint");
|
||||
}
|
||||
} else {
|
||||
inputEl.classList.add("is-invalid");
|
||||
entryDescription.textContent = result.hint;
|
||||
entryDescription.classList.add("ase-desc-error-hint");
|
||||
}
|
||||
});
|
||||
entryOperationArea.classList.add("ase-operation-area-expanded");
|
||||
entryOperationArea.appendChild(inputEl);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
entryContainerEl.appendChild(entryOperationArea);
|
||||
const isShow = entry.auraIf();
|
||||
if (!isShow) entryContainerEl.classList.add("aura-settings-entry-hidden");
|
||||
|
||||
if (entry.associateVal) {
|
||||
document.addEventListener(
|
||||
isPls ? "onPLSConfigUpdate" : "onHugoAuraConfigUpdate",
|
||||
(event) => {
|
||||
if (!entry.associateVal.includes(event.detail.path.join(".")))
|
||||
return;
|
||||
const cls = entryContainerEl.classList;
|
||||
const isShow = entry.auraIf();
|
||||
isShow
|
||||
? cls.remove("aura-settings-entry-hidden")
|
||||
: cls.add("aura-settings-entry-hidden");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
formEl.appendChild(entryContainerEl);
|
||||
renderChild(entry, formEl);
|
||||
}
|
||||
|
||||
const hrEl = document.createElement("hr");
|
||||
@@ -232,11 +503,8 @@ const settingsRenderer = (pendingEl, settingsObj, isPls = false) => {
|
||||
}
|
||||
pendingEl.appendChild(formEl);
|
||||
|
||||
const tooltipTriggerList = document.querySelectorAll(
|
||||
'[data-bs-toggle="tooltip"]'
|
||||
);
|
||||
[...tooltipTriggerList].map(
|
||||
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
global.__HUGO_AURA_GLOBAL__.utils.refreshBsTooltip(
|
||||
".aura-settings-entry-property-icon"
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -46,6 +46,15 @@
|
||||
flex: 0.75;
|
||||
}
|
||||
|
||||
.aura-settings-entry-operation-area.ase-operation-area-disabled {
|
||||
opacity: 0.25;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.aura-settings-entry-operation-area.ase-operation-area-disabled * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.aura-settings-entry-operation-area.form-switch {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
@@ -56,6 +65,33 @@
|
||||
border-bottom: 0.75px solid rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.aura-settings-preview-area-hr-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.aura-settings-preview-hr {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-bottom: 0.75px solid rgba(0, 0, 0, 0.25);
|
||||
width: -webkit-fill-available;
|
||||
}
|
||||
|
||||
.aura-settings-preview-area-hr-container p {
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
min-width: 2rem;
|
||||
opacity: 0.75;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.aura-settings-preview-area-container {
|
||||
width: 100%;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.aura-settings-entry-property-icon {
|
||||
margin-left: 0.5rem;
|
||||
font-size: 15px;
|
||||
@@ -69,6 +105,12 @@
|
||||
color: rgb(0, 106, 188);
|
||||
}
|
||||
|
||||
.aura-settings-entry-warning-icon {
|
||||
transform: rotate(180deg);
|
||||
display: inline-block;
|
||||
color: rgb(147, 0, 0);
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
||||
@keyframes invalidShake {
|
||||
|
||||
@@ -25,3 +25,7 @@ button.nav-link {
|
||||
.nav-underline {
|
||||
--bs-nav-underline-link-active-color: #0d6efd !important;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
--bs-tooltip-max-width: 400px !important;
|
||||
}
|
||||
|
||||
@@ -21,33 +21,60 @@ const def = {
|
||||
pageSelector: "#root",
|
||||
selectorMode: "appendChild",
|
||||
pageCSS: "ui/pages/config/config.css",
|
||||
},
|
||||
"Aura.UI.Assistant.Config.DisableLimitations": {
|
||||
active: false,
|
||||
pageURI:
|
||||
"ui/pages/configSubPages/disableLimitations/disableLimitations.html",
|
||||
pageScript:
|
||||
"ui/pages/configSubPages/disableLimitations/disableLimitations.js",
|
||||
pageSelector: ".aura-config-page-subpage-container",
|
||||
selectorMode: "appendChild",
|
||||
pageCSS:
|
||||
"ui/pages/configSubPages/disableLimitations/disableLimitations.css",
|
||||
},
|
||||
"Aura.UI.Assistant.Config.BehaviourCtrl": {
|
||||
active: false,
|
||||
pageURI: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.html",
|
||||
pageScript: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.js",
|
||||
pageSelector: ".aura-config-page-subpage-container",
|
||||
selectorMode: "appendChild",
|
||||
pageCSS: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.css",
|
||||
},
|
||||
"Aura.UI.Assistant.Config.BehaviourCtrl.PlsStatus": {
|
||||
active: false,
|
||||
pageURI: "ui/pages/configSubPages/behaviourCtrl/plsStatus.html",
|
||||
pageScript: "ui/pages/configSubPages/behaviourCtrl/plsStatus.js",
|
||||
pageSelector: "#status-subpage",
|
||||
selectorMode: "appendChild",
|
||||
pageCSS: "ui/pages/configSubPages/behaviourCtrl/plsStatus.css",
|
||||
childs: {
|
||||
DisableLimitations: {
|
||||
active: false,
|
||||
pageURI:
|
||||
"ui/pages/configSubPages/disableLimitations/disableLimitations.html",
|
||||
pageScript:
|
||||
"ui/pages/configSubPages/disableLimitations/disableLimitations.js",
|
||||
pageSelector: ".aura-config-page-subpage-container",
|
||||
selectorMode: "appendChild",
|
||||
pageCSS:
|
||||
"ui/pages/configSubPages/disableLimitations/disableLimitations.css",
|
||||
},
|
||||
BehaviourCtrl: {
|
||||
active: false,
|
||||
pageURI: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.html",
|
||||
pageScript: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.js",
|
||||
pageSelector: ".aura-config-page-subpage-container",
|
||||
selectorMode: "appendChild",
|
||||
pageCSS: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.css",
|
||||
childs: {
|
||||
AikariStatus: {
|
||||
active: false,
|
||||
pageURI: "ui/pages/configSubPages/behaviourCtrl/aikariStatus.html",
|
||||
pageScript: "ui/pages/configSubPages/behaviourCtrl/aikariStatus.js",
|
||||
pageSelector: "#status-subpage",
|
||||
selectorMode: "appendChild",
|
||||
pageCSS: "ui/pages/configSubPages/behaviourCtrl/aikariStatus.css",
|
||||
},
|
||||
DeviceSecurity: {
|
||||
childs: {
|
||||
FreezeOverridePreview: {
|
||||
active: false,
|
||||
pageURI:
|
||||
"ui/pages/configSubPages/behaviourCtrl/settings/previews/freezeOverridePreview/freezeOverridePreview.html",
|
||||
pageScript:
|
||||
"ui/pages/configSubPages/behaviourCtrl/settings/previews/freezeOverridePreview/freezeOverridePreview.js",
|
||||
pageSelector: "#freezeInfoReportOverridePreviewContainer",
|
||||
selectorMode: "appendChild",
|
||||
pageCSS:
|
||||
"ui/pages/configSubPages/behaviourCtrl/settings/previews/freezeOverridePreview/freezeOverridePreview.css",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Preferences: {
|
||||
active: false,
|
||||
pageURI: "ui/pages/configSubPages/preferences/preferences.html",
|
||||
pageScript: "ui/pages/configSubPages/preferences/preferences.js",
|
||||
pageSelector: ".aura-config-page-subpage-container",
|
||||
selectorMode: "appendChild",
|
||||
pageCSS: "ui/pages/configSubPages/preferences/preferences.css",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
globalStyles: [
|
||||
@@ -57,7 +84,11 @@ const def = {
|
||||
"ui/layui/css/layui.css",
|
||||
"ui/bootstrap/bootstrap.min.css",
|
||||
],
|
||||
globalJS: ["ui/js/global.js", "ui/bootstrap/bootstrap.bundle.min.js"],
|
||||
globalJS: [
|
||||
"ui/js/global.js",
|
||||
"ui/js/aikariListener.js",
|
||||
"ui/bootstrap/bootstrap.bundle.min.js",
|
||||
],
|
||||
onLoaded: `
|
||||
console.log('[HugoAura / UI / Hooks / Assistant] Page loaded.');
|
||||
`,
|
||||
|
||||
2
src/aura/ui/hookDefinitions/desktopAssistant.js
Executable file → Normal file
2
src/aura/ui/hookDefinitions/desktopAssistant.js
Executable file → Normal 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.');
|
||||
`,
|
||||
|
||||
402
src/aura/ui/js/aikariConnectionManager.js
Normal file
402
src/aura/ui/js/aikariConnectionManager.js
Normal 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);
|
||||
})();
|
||||
112
src/aura/ui/js/aikariListener.js
Normal file
112
src/aura/ui/js/aikariListener.js
Normal file
@@ -0,0 +1,112 @@
|
||||
(() => {
|
||||
const IPC_METHOD_BASE = "$aura.aikari";
|
||||
const REQUIRE_BASE = "../../aura/ui";
|
||||
const __SCOPE = "assistant";
|
||||
const {
|
||||
updateAikariStatusFromLocal,
|
||||
} = require(`${REQUIRE_BASE}/composables/aikariConfigManager`);
|
||||
|
||||
const setupListeners = () => {
|
||||
if (!global.ipcRenderer)
|
||||
global.ipcRenderer = require("electron").ipcRenderer;
|
||||
|
||||
ipcRenderer.on(
|
||||
`${IPC_METHOD_BASE}.post.onAikariStatsUpdate`,
|
||||
(_event, arg) => {
|
||||
global.__HUGO_AURA__.aikariStats = arg;
|
||||
|
||||
const event = new CustomEvent("onAikariStatsUpdate", {
|
||||
detail: {
|
||||
connected: arg.connected,
|
||||
},
|
||||
});
|
||||
|
||||
document.dispatchEvent(event);
|
||||
|
||||
const settingsEntries = document.getElementsByClassName(
|
||||
"aura-settings-entry"
|
||||
);
|
||||
if (settingsEntries.length > 0) {
|
||||
Array.from(settingsEntries).forEach((entry) => {
|
||||
entry.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ipcRenderer.on(
|
||||
`${IPC_METHOD_BASE}.post.onAikariSettingsUpdate`,
|
||||
(_event, arg) => {
|
||||
global.__HUGO_AURA__.aikariSettings = arg;
|
||||
|
||||
const event = new CustomEvent("onAikariConfigUpdate", {
|
||||
detail: {
|
||||
path: ["root", "settings"],
|
||||
value: arg,
|
||||
},
|
||||
});
|
||||
|
||||
document.dispatchEvent(event);
|
||||
|
||||
const settingsEntries = document.getElementsByClassName(
|
||||
"aura-settings-entry"
|
||||
);
|
||||
if (settingsEntries.length > 0) {
|
||||
Array.from(settingsEntries).forEach((entry) => {
|
||||
entry.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ipcRenderer.on(
|
||||
`${IPC_METHOD_BASE}.post.onAikariRulesUpdate`,
|
||||
(_event, arg) => {
|
||||
global.__HUGO_AURA__.aikariRules = arg;
|
||||
|
||||
const event = new CustomEvent("onAikariConfigUpdate", {
|
||||
detail: {
|
||||
path: ["root", "ruleSettings"],
|
||||
value: arg,
|
||||
},
|
||||
});
|
||||
|
||||
document.dispatchEvent(event);
|
||||
|
||||
const settingsEntries = document.getElementsByClassName(
|
||||
"aura-settings-entry"
|
||||
);
|
||||
if (settingsEntries.length > 0) {
|
||||
Array.from(settingsEntries).forEach((entry) => {
|
||||
entry.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ipcRenderer.on(
|
||||
`${IPC_METHOD_BASE}.post.updateRetryStatus`,
|
||||
(_event, arg) => {
|
||||
const event = new CustomEvent("onAikariStatsUpdate", {
|
||||
detail: {
|
||||
connected: arg.success,
|
||||
},
|
||||
});
|
||||
|
||||
document.dispatchEvent(event);
|
||||
|
||||
if (
|
||||
global.__HUGO_AURA_LOADER__["Aura.UI.Assistant.Config.BehaviourCtrl"]
|
||||
.active
|
||||
) {
|
||||
setTimeout(() => {
|
||||
global.__HUGO_AURA_GLOBAL__.utils.refreshBsTooltip();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
updateAikariStatusFromLocal();
|
||||
setupListeners();
|
||||
})();
|
||||
@@ -1,9 +1,43 @@
|
||||
(() => {
|
||||
/* Util: Sleep */
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
/* Util: BootStrap Tooltip Ctrl */
|
||||
let tooltipTriggerCache = null;
|
||||
const refreshBsTooltip = (selector = '[data-bs-toggle="tooltip"]') => {
|
||||
if (tooltipTriggerCache) {
|
||||
[...tooltipTriggerCache].map((el) => {
|
||||
if (bootstrap.Tooltip.getInstance(el)) {
|
||||
bootstrap.Tooltip.getInstance(el).disable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const tooltipTriggerList = document.querySelectorAll(selector);
|
||||
tooltipTriggerCache = tooltipTriggerList;
|
||||
[...tooltipTriggerList].map(
|
||||
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
);
|
||||
};
|
||||
|
||||
const createOnLeaveEvtListener = (
|
||||
channel,
|
||||
pendingRmEvtListener,
|
||||
leaveEvt = "onCurConfigPageLeave"
|
||||
) => {
|
||||
const rmEvtListener = (event) => {
|
||||
document.removeEventListener(channel, pendingRmEvtListener);
|
||||
document.removeEventListener(leaveEvt, rmEvtListener);
|
||||
};
|
||||
document.addEventListener(leaveEvt, rmEvtListener);
|
||||
return rmEvtListener;
|
||||
};
|
||||
|
||||
if (!window.__HUGO_AURA_GLOBAL__) window.__HUGO_AURA_GLOBAL__ = {};
|
||||
|
||||
window.__HUGO_AURA_GLOBAL__.utils = {
|
||||
sleep,
|
||||
refreshBsTooltip,
|
||||
createOnLeaveEvtListener,
|
||||
};
|
||||
})();
|
||||
|
||||
23
src/aura/ui/js/pageGlobal/desktopAssistant.js
Normal file
23
src/aura/ui/js/pageGlobal/desktopAssistant.js
Normal 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();
|
||||
})();
|
||||
@@ -1,192 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
const IPC_METHOD_BASE = "$aura.pls";
|
||||
const REQUIRE_BASE = "../../aura/ui";
|
||||
const __SCOPE = "desktopAssistant";
|
||||
|
||||
const { pushMsgHandler } = require(`${REQUIRE_BASE}/pls/pushHandler`);
|
||||
|
||||
/** @type {number} */
|
||||
let failedCounter = 0;
|
||||
/** @type {boolean} */
|
||||
let isErrorOccurred = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {WebSocket} */
|
||||
const plsWs = new WebSocket(
|
||||
`wss://pls.hugoaura.local:22077/?auth=${authToken}`
|
||||
);
|
||||
|
||||
plsWs.onopen = () => {
|
||||
callback(true, plsWs);
|
||||
};
|
||||
|
||||
plsWs.onerror = () => {
|
||||
isErrorOccurred = true;
|
||||
failedCounter += 1;
|
||||
callback(false, plsWs);
|
||||
};
|
||||
|
||||
plsWs.onclose = () => {
|
||||
console.error(
|
||||
"[HugoAura / UI / PLS Manager / ERROR] WebSocket connection closed."
|
||||
);
|
||||
if (isErrorOccurred) return;
|
||||
failedCounter += 1;
|
||||
callback(false, plsWs);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {WebSocket} wsObj
|
||||
*/
|
||||
const registerSendReqListener = (wsObj) => {
|
||||
global.ipcRenderer.on(
|
||||
`${IPC_METHOD_BASE}.ws.post.onReqSendMsg`,
|
||||
/**
|
||||
*
|
||||
* @param {Event} _evt
|
||||
* @param {any} arg
|
||||
*/
|
||||
(_evt, arg) => {
|
||||
wsObj.send(JSON.stringify(arg));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {boolean} result
|
||||
* @param {WebSocket} wsObj
|
||||
* @returns
|
||||
*/
|
||||
const connectionResultCallback = (result, wsObj) => {
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats.launched = result;
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats.connected = result;
|
||||
global.ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.updatePlsStats`,
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats
|
||||
);
|
||||
if (!result) {
|
||||
console.error(
|
||||
`[HugoAura / UI / PLS Manager / ERROR] Failed connecting to PLS WebSocket server, retrying ...`
|
||||
);
|
||||
createPlsConnection(
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats.authToken,
|
||||
connectionResultCallback
|
||||
);
|
||||
return;
|
||||
}
|
||||
global.__HUGO_AURA_GLOBAL__.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 () => {
|
||||
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}.getPlsFolderExists`)
|
||||
).data.isExists;
|
||||
updatedPlsStats.installed = isPlsFolderExists;
|
||||
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats = updatedPlsStats;
|
||||
console.debug(
|
||||
"[HugoAura / UI / PLS Manager / DEBUG] Updated plsStats:",
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats
|
||||
);
|
||||
|
||||
global.ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.updatePlsStats`,
|
||||
updatedPlsStats
|
||||
);
|
||||
|
||||
const startConnPls = () => {
|
||||
createPlsConnection(updatedPlsStats.authToken, connectionResultCallback);
|
||||
};
|
||||
|
||||
if (updatedPlsStats.detached && updatedPlsStats.installed) {
|
||||
startConnPls();
|
||||
}
|
||||
|
||||
global.ipcRenderer.on(`${IPC_METHOD_BASE}.post.onPlsLaunched`, (_event) => {
|
||||
setTimeout(() => {
|
||||
startConnPls();
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
|
||||
const onSetup = () => {
|
||||
if (!global.global.ipcRenderer) {
|
||||
// @ts-ignore
|
||||
global.global.ipcRenderer = require("electron").global.ipcRenderer;
|
||||
}
|
||||
|
||||
initPlsConnection();
|
||||
};
|
||||
|
||||
(() => {
|
||||
onSetup();
|
||||
})();
|
||||
@@ -1,486 +1,27 @@
|
||||
/* General */
|
||||
|
||||
#aura-container-Aura-UI-Assistant-Config {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.aura-config-page-root {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: url("../../../../app.asar/public/ae247697b4639c92bd008d0ea7d13b53.png");
|
||||
/* 这里不用 background-size: cover; 的效果反而更舒服一些... */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-root-inactive {
|
||||
opacity: 0;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
@import url("./css/general.css");
|
||||
|
||||
/* Header */
|
||||
|
||||
.aura-config-page-header-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
color: white;
|
||||
z-index: 12000;
|
||||
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area .iconfont {
|
||||
font-size: 24px;
|
||||
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area .iconfont:hover {
|
||||
opacity: 0.75;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area .iconfont:active {
|
||||
opacity: 0.375;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area p {
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area.header-collapsed {
|
||||
transform: translateY(-1rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-app-bar {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
@import url("./css/header.css");
|
||||
|
||||
/* Status */
|
||||
|
||||
.aura-config-page-status-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
align-self: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
opacity: 1;
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-status-container-hidden {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-status-main,
|
||||
.aura-config-page-status-description {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.aura-config-page-status-description {
|
||||
margin-top: 0.5rem;
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-status-description.status-description-hidden {
|
||||
transform: translateY(-2rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-status-description p {
|
||||
font-size: 18px;
|
||||
margin-left: 15px;
|
||||
margin-top: -2px;
|
||||
color: white;
|
||||
font-family: "Consolas", "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
.aura-config-page-status-description i {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.aura-config-page-central-aura-logo {
|
||||
margin: 0.5rem 3rem;
|
||||
width: 17.5%;
|
||||
}
|
||||
|
||||
.aura-config-hr-vertical {
|
||||
height: 3.75rem;
|
||||
width: 1px;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.aura-config-page-status-el {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Consolas", monospace;
|
||||
color: white;
|
||||
|
||||
/*
|
||||
.version-type {
|
||||
content: "I want to use scss plz 😇"
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
.aura-config-page-status-side {
|
||||
height: 30%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
|
||||
transition: transform 0.5s, opacity 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-status-side.left-side {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.aura-config-page-status-side.left-side.status-side-hidden {
|
||||
transform: translateX(5rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-status-side.right-side {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.aura-config-page-status-side.right-side.status-side-hidden {
|
||||
transform: translateX(-5rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-status-el .version-type {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.aura-config-page-status-el .version-content {
|
||||
font-size: 16px;
|
||||
margin-top: 5px;
|
||||
opacity: 0.625;
|
||||
}
|
||||
@import url("./css/status.css");
|
||||
|
||||
/* Operation */
|
||||
|
||||
.aura-config-page-operation-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
@import url("./css/operation.css");
|
||||
|
||||
.aura-config-page-operation-area::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
/* Config Status Notify */
|
||||
|
||||
.aura-config-page-operation-area.subpage-expanded {
|
||||
flex: 15;
|
||||
}
|
||||
|
||||
.aura-config-page-subpage-container {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
background-color: rgba(255, 255, 255, 0.825);
|
||||
z-index: 6000;
|
||||
overflow-y: scroll;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-subpage-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-area.subpage-expanded
|
||||
.aura-config-page-subpage-container {
|
||||
height: calc(100% - 40px - 4rem);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-container.hide-other-operations
|
||||
.aura-config-page-operation-el:not(.preserve-operation) {
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-container.hide-other-operations
|
||||
.aura-config-page-operation-el.preserve-operation {
|
||||
flex: 0.25;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
max-width: 25%;
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
|
||||
transition: opacity 0.5s, transform 0.5s,
|
||||
max-width cubic-bezier(0, 0.42, 0.18, 1) 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-hidden {
|
||||
transform: translateY(2rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show
|
||||
.aura-config-page-operation-body {
|
||||
opacity: 1;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]
|
||||
.aura-config-page-operation-body {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show:not(.preserve-operation):hover
|
||||
.aura-config-page-operation-body {
|
||||
opacity: 0.625;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover
|
||||
.aura-config-page-operation-body {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show:not(.preserve-operation):active
|
||||
.aura-config-page-operation-body {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]::after {
|
||||
content: "别急嘛, 还在开发呢...";
|
||||
font-size: 16px;
|
||||
opacity: 0;
|
||||
color: white;
|
||||
position: absolute;
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover::after,
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:active::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el img {
|
||||
max-width: 40px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el .config-operation-title {
|
||||
color: white;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el .config-operation-description {
|
||||
color: white;
|
||||
opacity: 0.75;
|
||||
font-size: small;
|
||||
}
|
||||
@import url("./css/configStatusNotify.css");
|
||||
|
||||
/* Auth Dialog */
|
||||
|
||||
.aura-config-page-auth-dialog-area {
|
||||
position: absolute;
|
||||
height: calc(100% - 40px);
|
||||
width: 100%;
|
||||
top: 40px;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10000;
|
||||
background-color: rgba(255, 255, 255, 0.35);
|
||||
|
||||
opacity: 1;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-auth-dialog-area.blur-enabled {
|
||||
height: 100%;
|
||||
top: 0;
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(5px);
|
||||
filter: blur(0.1px);
|
||||
/* ↑ 似乎会导致性能问题 */
|
||||
}
|
||||
|
||||
.acp-ada-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.acp-ada-hidden.blur-enabled {
|
||||
backdrop-filter: blur(0.1px);
|
||||
filter: unset;
|
||||
}
|
||||
|
||||
.aura-config-page-auth-dialog {
|
||||
height: 40%;
|
||||
width: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.625);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.acp-auth-dialog-title {
|
||||
font-size: x-large;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
#acp-auth-user-input {
|
||||
max-width: 50%;
|
||||
/* background-color: rgba(255, 255, 255, 0.5); */
|
||||
border-radius: 35px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#acp-auth-user-input.invalid {
|
||||
animation: invalidShake 0.6s linear;
|
||||
}
|
||||
|
||||
.acp-auth-confirm-btn {
|
||||
background-color: transparent;
|
||||
border-radius: 35px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.acp-auth-confirm-btn .layui-icon {
|
||||
font-size: 24px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
@import url("./css/authDialog.css");
|
||||
|
||||
/* Toast */
|
||||
|
||||
.aura-config-page-toast-area {
|
||||
z-index: 9000;
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast {
|
||||
--bs-toast-border-width: 0 !important;
|
||||
--bs-toast-bg: #fff !important;
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast-header {
|
||||
background-color: rgb(255, 234, 202);
|
||||
|
||||
border-top-left-radius: var(--bs-toast-border-radius);
|
||||
border-top-right-radius: var(--bs-toast-border-radius);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast.acp-toast-emerg .toast-header {
|
||||
background-color: rgb(255, 202, 202);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast-header * {
|
||||
color: rgba(234, 126, 14, 0.85);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast.acp-toast-emerg .toast-header * {
|
||||
color: rgba(234, 65, 14, 0.85);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast-body p {
|
||||
margin-bottom: var(--bs-toast-padding-x);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast-header .layui-icon {
|
||||
font-weight: bolder;
|
||||
margin-right: 0.5rem;
|
||||
font-size: 18px;
|
||||
}
|
||||
@import url("./css/toast.css");
|
||||
|
||||
@@ -1,13 +1,63 @@
|
||||
<div class="aura-config-page-root-inactive aura-config-page-root">
|
||||
<div
|
||||
class="aura-config-page-root-inactive aura-config-page-root"
|
||||
style="display: none"
|
||||
>
|
||||
<!-- display: none 用于防止 CSS 还未加载完成时, 用户看到错乱的样式 -->
|
||||
<div class="header-collapsed aura-config-page-header-area">
|
||||
<div class="aura-config-page-app-bar" style="-webkit-app-region: drag">
|
||||
<div
|
||||
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavBack()"
|
||||
style="-webkit-app-region: no-drag; z-index: 2000"
|
||||
style="-webkit-app-region: no-drag; z-index: 2000; margin-right: 0.1rem"
|
||||
>
|
||||
<i class="iconfont"></i>
|
||||
<!-- Chevron Left Icon -->
|
||||
</div>
|
||||
<p>雨光之环</p>
|
||||
<div
|
||||
class="aura-config-page-app-bar-hr-vertical"
|
||||
id="auraConfigPageAppBarVerticalHr"
|
||||
></div>
|
||||
<div class="aura-config-page-app-bar-spacer space-none"></div>
|
||||
<div
|
||||
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavHome()"
|
||||
style="
|
||||
-webkit-app-region: no-drag;
|
||||
z-index: 2000;
|
||||
margin-left: 6px;
|
||||
margin-right: 0.5rem;
|
||||
"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 24 24"
|
||||
class="iconfont"
|
||||
style="margin-top: -1.35px"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M6 19h3.692v-5.884h4.616V19H18v-9l-6-4.538L6 10zm-1 1V9.5l7-5.288L19 9.5V20h-5.692v-5.884h-2.616V20zm7-7.77"
|
||||
stroke-width="0.5"
|
||||
stroke="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="aura-config-page-app-bar-spacer"></div>
|
||||
<div
|
||||
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.minimizeWindow()"
|
||||
style="-webkit-app-region: no-drag; z-index: 2000"
|
||||
>
|
||||
<i class="iconfont"></i>
|
||||
<!-- Minimize Icon -->
|
||||
</div>
|
||||
<div
|
||||
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.closeWindow()"
|
||||
style="-webkit-app-region: no-drag; z-index: 2000; margin-left: 0.5rem"
|
||||
>
|
||||
<i class="iconfont"></i>
|
||||
<!-- Failed / Cancel Icon -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -66,6 +116,28 @@
|
||||
<div class="aura-config-page-operation-area">
|
||||
<div class="aura-config-page-subpage-container"></div>
|
||||
<div class="aura-config-page-operation-container">
|
||||
<div class="acp-config-status-notify hidden fully-hidden">
|
||||
<div class="acp-config-status-notify-area">
|
||||
<div class="acp-config-status-notify-main-content">
|
||||
<i class="layui-icon layui-icon-component acsn-main-icon"></i>
|
||||
<p class="acsn-main-title">修改的配置暂未保存</p>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-primary btn-sm acsn-save-btn"
|
||||
onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.handleSaveConfig()"
|
||||
>
|
||||
保存配置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="acp-config-status-notify-success acsn-success-hidden acsn-success-fully-hidden"
|
||||
>
|
||||
<i class="layui-icon layui-icon-release"></i>
|
||||
<p>保存成功</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="operation-el-hidden aura-config-page-operation-el"
|
||||
onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig('disableLimitations', true)"
|
||||
@@ -73,21 +145,20 @@
|
||||
<div class="aura-config-page-operation-body">
|
||||
<img src="../../aura/ui/static/config/no_limitations.svg" />
|
||||
<div>
|
||||
<p class="config-operation-title">限制解除</p>
|
||||
<p class="config-operation-description">禁用密码、关闭冰点</p>
|
||||
<p class="config-operation-title">行为改写</p>
|
||||
<p class="config-operation-description">覆写密码、禁用能力</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="operation-el-hidden aura-config-page-operation-el"
|
||||
aura-disabled="true"
|
||||
onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig('behaviourCtrl', true)"
|
||||
>
|
||||
<!-- Still WIP -->
|
||||
<!-- onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig('behaviourCtrl', true)" -->
|
||||
<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>
|
||||
@@ -104,12 +175,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="operation-el-hidden aura-config-page-operation-el">
|
||||
<div
|
||||
class="operation-el-hidden aura-config-page-operation-el"
|
||||
onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig('preferences', true)"
|
||||
>
|
||||
<div class="aura-config-page-operation-body">
|
||||
<img src="../../aura/ui/static/config/about.svg" />
|
||||
<div>
|
||||
<p class="config-operation-title">关于项目</p>
|
||||
<p class="config-operation-description">使用文档、获取帮助</p>
|
||||
<p class="config-operation-title">偏好设置</p>
|
||||
<p class="config-operation-description">Aura 设置、关于项目</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -119,6 +193,7 @@
|
||||
<div
|
||||
class="aura-config-page-auth-dialog-area acp-ada-hidden"
|
||||
style="display: none"
|
||||
aura-cancel="true"
|
||||
>
|
||||
<div class="aura-config-page-auth-dialog">
|
||||
<p class="acp-auth-dialog-title">验证您的身份</p>
|
||||
@@ -131,12 +206,21 @@
|
||||
id="acp-auth-user-input"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="acp-auth-confirm-btn"
|
||||
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword()"
|
||||
>
|
||||
<i class="layui-icon layui-icon-right"></i>
|
||||
</button>
|
||||
<div class="acp-auth-btns-container">
|
||||
<button
|
||||
class="acp-auth-cancel-btn"
|
||||
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavBack()"
|
||||
>
|
||||
<i class="layui-icon layui-icon-return"></i>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="acp-auth-confirm-btn"
|
||||
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword()"
|
||||
>
|
||||
<i class="layui-icon layui-icon-ok"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -151,13 +235,22 @@
|
||||
<div class="toast-header">
|
||||
<i class="layui-icon layui-icon-tips"></i>
|
||||
<strong class="me-auto">重载页面以应用设置</strong>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="toast"
|
||||
></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
<p>请重载当前窗口以应用修改的设置</p>
|
||||
<p>已修改的配置将自动保存</p>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
onclick="window.location.reload()"
|
||||
onclick="(() => {
|
||||
global.__HUGO_AURA_CONFIG_MGR__.writeConfig(global.__HUGO_AURA_CONFIG__);
|
||||
window.location.reload();
|
||||
})()"
|
||||
>
|
||||
重载页面
|
||||
</button>
|
||||
@@ -175,13 +268,22 @@
|
||||
<div class="toast-header">
|
||||
<i class="layui-icon layui-icon-tips"></i>
|
||||
<strong class="me-auto">重启进程以应用设置</strong>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="toast"
|
||||
></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
<p>请重启 Electron 进程以应用修改的设置</p>
|
||||
<p>已修改的配置将自动保存</p>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
onclick="ipcRenderer.invoke('$aura.base.restartApplication')"
|
||||
onclick="(() => {
|
||||
global.__HUGO_AURA_CONFIG_MGR__.writeConfig(global.__HUGO_AURA_CONFIG__);
|
||||
ipcRenderer.invoke('$aura.base.restartApplication')
|
||||
})()"
|
||||
>
|
||||
重启进程
|
||||
</button>
|
||||
@@ -191,22 +293,28 @@
|
||||
|
||||
<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"
|
||||
data-bs-dismiss="toast"
|
||||
></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>
|
||||
|
||||
@@ -1,20 +1,64 @@
|
||||
global.__HUGO_AURA_UI_REACTIVES__.config = {
|
||||
isInSubPage: false,
|
||||
currentActiveSubPage: "",
|
||||
authenticated: false,
|
||||
isConfigPendingWrite: false,
|
||||
};
|
||||
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config = {
|
||||
closeWindow: async () => {
|
||||
if (global.__HUGO_AURA_UI_REACTIVES__.config.isConfigPendingWrite) {
|
||||
await global.__HUGO_AURA_UI_FUNCTIONS__.config.handleSaveConfig();
|
||||
}
|
||||
|
||||
global.ipcRenderer.send("$aura.base.closeWindow", {
|
||||
targetWindowKey: "assistant",
|
||||
});
|
||||
},
|
||||
|
||||
minimizeWindow: () => {
|
||||
global.ipcRenderer.send("$aura.base.minimizeWindow", {
|
||||
targetWindowKey: "assistant",
|
||||
});
|
||||
},
|
||||
|
||||
handleNavBack: () => {
|
||||
if (global.__HUGO_AURA_UI_REACTIVES__.config.isInSubPage) {
|
||||
const acsDialogAreaEl = document.getElementsByClassName(
|
||||
"aura-config-page-auth-dialog-area"
|
||||
)[0];
|
||||
if (!Array.from(acsDialogAreaEl.classList).includes("acp-ada-hidden")) {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.hideAndResetAuthDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
if (global.__HUGO_AURA_UI_REACTIVES__.config.isConfigPendingWrite) {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.handleSaveConfig();
|
||||
}
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig(
|
||||
global.__HUGO_AURA_UI_REACTIVES__.config.currentActiveSubPage,
|
||||
false
|
||||
);
|
||||
const onLeaveEvent = new CustomEvent("onCurConfigPageLeave");
|
||||
document.dispatchEvent(onLeaveEvent);
|
||||
} else {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.hideConfigPage();
|
||||
}
|
||||
},
|
||||
|
||||
handleNavHome: async () => {
|
||||
if (global.__HUGO_AURA_UI_REACTIVES__.config.isConfigPendingWrite) {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.handleSaveConfig();
|
||||
}
|
||||
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.hideConfigPage();
|
||||
|
||||
setTimeout(() => {
|
||||
const onLeaveEvent = new CustomEvent("onCurConfigPageLeave");
|
||||
document.dispatchEvent(onLeaveEvent);
|
||||
}, 500);
|
||||
},
|
||||
|
||||
hideConfigPage: async () => {
|
||||
const defaultHeader = document.getElementsByClassName(
|
||||
"index__header__16DmR2a5"
|
||||
@@ -34,6 +78,7 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
|
||||
|
||||
toggleSubConfig: (subPage, side) => {
|
||||
if (side === global.__HUGO_AURA_UI_REACTIVES__.config.isInSubPage) return;
|
||||
if (!global.__HUGO_AURA_UI_REACTIVES__.config.authenticated) return;
|
||||
if (!side) {
|
||||
side = !global.__HUGO_AURA_UI_REACTIVES__.config.isInSubPage;
|
||||
}
|
||||
@@ -61,11 +106,20 @@ 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);
|
||||
}
|
||||
break;
|
||||
case "plugins":
|
||||
// To Be Done
|
||||
preserveOperationIdx = 2;
|
||||
pendingSubPageId = "Aura.UI.Assistant.Config.Plugins";
|
||||
break;
|
||||
case "preferences":
|
||||
preserveOperationIdx = 3;
|
||||
pendingSubPageId = "Aura.UI.Assistant.Config.Preferences";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -145,13 +199,9 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
|
||||
encPasswd ===
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordWithSalt
|
||||
) {
|
||||
const acsDialogAreaEl = document.getElementsByClassName(
|
||||
"aura-config-page-auth-dialog-area"
|
||||
)[0];
|
||||
acsDialogAreaEl.classList.add("acp-ada-hidden");
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
|
||||
acsDialogAreaEl.style = "display: none;";
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(250);
|
||||
await global.__HUGO_AURA_UI_FUNCTIONS__.config.hideAndResetAuthDialog();
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(250);
|
||||
global.__HUGO_AURA_UI_REACTIVES__.config.authenticated = true;
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.showSecondPhaseAnim();
|
||||
return true;
|
||||
} else {
|
||||
@@ -159,6 +209,127 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
hideAndResetAuthDialog: async () => {
|
||||
const acsDialogAreaEl = document.getElementsByClassName(
|
||||
"aura-config-page-auth-dialog-area"
|
||||
)[0];
|
||||
const acpAppBarEl = document.getElementsByClassName(
|
||||
"aura-config-page-header-area"
|
||||
)[0];
|
||||
const acpDialogTitleEl = document.getElementsByClassName(
|
||||
"acp-auth-dialog-title"
|
||||
)[0];
|
||||
const acpDialogConfirmBtnEl = document.getElementsByClassName(
|
||||
"acp-auth-confirm-btn"
|
||||
)[0];
|
||||
const acpDialogCancelBtnEl = document.getElementsByClassName(
|
||||
"acp-auth-cancel-btn"
|
||||
)[0];
|
||||
const inputEl = document.getElementById("acp-auth-user-input");
|
||||
acsDialogAreaEl.classList.add("acp-ada-hidden");
|
||||
acpAppBarEl.classList.remove("color-reverse");
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
|
||||
acsDialogAreaEl.style = "display: none;";
|
||||
acpDialogTitleEl.textContent = "验证您的身份";
|
||||
inputEl.value = "";
|
||||
inputEl.classList.remove("invalid");
|
||||
inputEl.classList.remove("is-invalid");
|
||||
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.resetAuthDialogInputElOnSubmit(
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword
|
||||
);
|
||||
|
||||
acpDialogConfirmBtnEl.onclick = (_evt) => {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword();
|
||||
};
|
||||
acpDialogCancelBtnEl.onclick = (_evt) => {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavBack();
|
||||
};
|
||||
},
|
||||
|
||||
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"
|
||||
)[0];
|
||||
acsnRootEl.classList.remove("fully-hidden");
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(10);
|
||||
acsnRootEl.classList.remove("hidden");
|
||||
return true;
|
||||
},
|
||||
|
||||
handleSaveConfig: async () => {
|
||||
const result = global.__HUGO_AURA_CONFIG_MGR__.writeConfig(
|
||||
global.__HUGO_AURA_CONFIG__
|
||||
);
|
||||
|
||||
if (result) {
|
||||
global.__HUGO_AURA_UI_REACTIVES__.config.isConfigPendingWrite = false;
|
||||
const acsnRootEl = document.getElementsByClassName(
|
||||
"acp-config-status-notify"
|
||||
)[0];
|
||||
const acsnMainContentEl = document.getElementsByClassName(
|
||||
"acp-config-status-notify-main-content"
|
||||
)[0];
|
||||
const acsnSuccessEl = document.getElementsByClassName(
|
||||
"acp-config-status-notify-success"
|
||||
)[0];
|
||||
const acsnAreaEl = document.getElementsByClassName(
|
||||
"acp-config-status-notify-area"
|
||||
)[0];
|
||||
acsnMainContentEl.classList.add("acsn-main-content-hidden");
|
||||
acsnAreaEl.classList.add("transparent");
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(250);
|
||||
acsnMainContentEl.classList.add("acsn-main-content-fully-hidden");
|
||||
acsnSuccessEl.classList.remove("acsn-success-fully-hidden");
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(50);
|
||||
acsnSuccessEl.classList.remove("acsn-success-hidden");
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(1500);
|
||||
acsnRootEl.classList.add("hidden");
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(500);
|
||||
acsnRootEl.classList.add("fully-hidden");
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(10);
|
||||
// Reset class
|
||||
acsnMainContentEl.className = "acp-config-status-notify-main-content";
|
||||
acsnAreaEl.className = "acp-config-status-notify-area";
|
||||
acsnSuccessEl.className =
|
||||
"acp-config-status-notify-success acsn-success-hidden acsn-success-fully-hidden";
|
||||
return true;
|
||||
} else {
|
||||
// TODO: Error handling
|
||||
}
|
||||
},
|
||||
|
||||
initCustomUIProps: async (refresh = false) => {
|
||||
const verticalHrEl = document.getElementById(
|
||||
"auraConfigPageAppBarVerticalHr"
|
||||
);
|
||||
const spacerElArr = document.getElementsByClassName(
|
||||
"aura-config-page-app-bar-spacer"
|
||||
);
|
||||
if (
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.appearance.appBar
|
||||
.actionBtnsOnRight
|
||||
) {
|
||||
verticalHrEl.classList.add("hidden");
|
||||
spacerElArr[0].classList.remove("space-none");
|
||||
spacerElArr[1].classList.add("space-none");
|
||||
} else if (refresh) {
|
||||
verticalHrEl.classList.remove("hidden");
|
||||
spacerElArr[0].classList.add("space-none");
|
||||
spacerElArr[1].classList.remove("space-none");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
(() => {
|
||||
@@ -203,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;
|
||||
});
|
||||
@@ -218,21 +390,23 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordEnabled;
|
||||
|
||||
if (!isAuthEnabled) {
|
||||
global.__HUGO_AURA_UI_REACTIVES__.config.authenticated = true;
|
||||
showOperationsAnimation();
|
||||
} else {
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(50);
|
||||
const acsDialogAreaEl = document.getElementsByClassName(
|
||||
"aura-config-page-auth-dialog-area"
|
||||
)[0];
|
||||
const acpAppBarEl = document.getElementsByClassName(
|
||||
"aura-config-page-header-area"
|
||||
)[0];
|
||||
acsDialogAreaEl.style = "";
|
||||
if (
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.appearance
|
||||
.enablePasswdDialogBlur
|
||||
) {
|
||||
acsDialogAreaEl.classList.add("blur-enabled");
|
||||
}
|
||||
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");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -257,6 +431,7 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
|
||||
};
|
||||
|
||||
const onMounted = () => {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.initCustomUIProps();
|
||||
applyVersionInfo();
|
||||
|
||||
showAnimation();
|
||||
|
||||
95
src/aura/ui/pages/config/css/authDialog.css
Executable file
95
src/aura/ui/pages/config/css/authDialog.css
Executable file
@@ -0,0 +1,95 @@
|
||||
.aura-config-page-auth-dialog-area {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10000;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
|
||||
opacity: 1;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.acp-ada-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-auth-dialog {
|
||||
height: 40%;
|
||||
width: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.75);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.acp-auth-dialog-title {
|
||||
font-size: x-large;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
#acp-auth-user-input {
|
||||
max-width: 50%;
|
||||
/* background-color: rgba(255, 255, 255, 0.5); */
|
||||
border-radius: 35px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#acp-auth-user-input.invalid {
|
||||
animation: invalidShake 0.6s linear;
|
||||
}
|
||||
|
||||
.acp-auth-btns-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.acp-auth-btns-container button {
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.acp-auth-btns-container button:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.acp-auth-btns-container button:active {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.acp-auth-confirm-btn,
|
||||
.acp-auth-cancel-btn {
|
||||
border-radius: 35px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.acp-auth-confirm-btn {
|
||||
background: linear-gradient(135deg, #218fff 0%, #3fbaff 100%);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.acp-auth-confirm-btn .layui-icon {
|
||||
font-size: 24px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.acp-auth-cancel-btn .layui-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.acp-auth-cancel-btn {
|
||||
background-color: transparent;
|
||||
margin-right: 3rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.aura-config-page-auth-dialog-area[aura-cancel="false"] .acp-auth-cancel-btn {
|
||||
display: none;
|
||||
}
|
||||
114
src/aura/ui/pages/config/css/configStatusNotify.css
Executable file
114
src/aura/ui/pages/config/css/configStatusNotify.css
Executable file
@@ -0,0 +1,114 @@
|
||||
.acp-config-status-notify {
|
||||
position: absolute;
|
||||
height: calc(40px + 4rem);
|
||||
width: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(5px);
|
||||
filter: blur(0.1px);
|
||||
z-index: 15000;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.acp-config-status-notify.hidden {
|
||||
backdrop-filter: blur(0.1px);
|
||||
filter: unset;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.acp-config-status-notify.fully-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-area {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
transition: background-color 0.25s;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-area.transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-main-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* margin-bottom: 0.75rem; */
|
||||
|
||||
opacity: 1;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-main-content.acsn-main-content-hidden {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-main-content.acsn-main-content-fully-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-area .acsn-main-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-area .acsn-main-title {
|
||||
font-size: large;
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 2.25rem;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-area .acsn-save-btn {
|
||||
margin-top: 2px; /* 可能是中文导致的 */
|
||||
|
||||
opacity: 1;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-area .acsn-save-btn:active {
|
||||
opacity: 0.625;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-success {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
|
||||
color: white;
|
||||
|
||||
opacity: 1;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-success.acsn-success-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-success.acsn-success-fully-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-success .layui-icon {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.acp-config-status-notify-success p {
|
||||
font-size: large;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
33
src/aura/ui/pages/config/css/general.css
Executable file
33
src/aura/ui/pages/config/css/general.css
Executable file
@@ -0,0 +1,33 @@
|
||||
#aura-container-Aura-UI-Assistant-Config {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.aura-config-page-root {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: url("../../../../../app.asar/public/ae247697b4639c92bd008d0ea7d13b53.png");
|
||||
/* 这里不用 background-size: cover; 的效果反而更舒服一些... */
|
||||
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-root-inactive {
|
||||
opacity: 0;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
76
src/aura/ui/pages/config/css/header.css
Executable file
76
src/aura/ui/pages/config/css/header.css
Executable file
@@ -0,0 +1,76 @@
|
||||
.aura-config-page-header-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
color: white;
|
||||
z-index: 12000;
|
||||
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area.color-reverse {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.aura-config-page-header-area.color-reverse
|
||||
.aura-config-page-app-bar-hr-vertical {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.aura-config-page-header-area .iconfont {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area .iconfont:hover {
|
||||
opacity: 0.75;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area .iconfont:active {
|
||||
opacity: 0.375;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area p {
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.aura-config-page-header-area.header-collapsed {
|
||||
transform: translateY(-1rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-app-bar {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aura-config-page-app-bar-spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.aura-config-page-app-bar-spacer.space-none {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-app-bar-hr-vertical {
|
||||
position: relative;
|
||||
margin-left: 8px;
|
||||
width: 1px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
height: 12px;
|
||||
transition: background 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-app-bar-hr-vertical.hidden {
|
||||
display: none;
|
||||
}
|
||||
155
src/aura/ui/pages/config/css/operation.css
Executable file
155
src/aura/ui/pages/config/css/operation.css
Executable file
@@ -0,0 +1,155 @@
|
||||
.aura-config-page-operation-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-area::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-area.subpage-expanded {
|
||||
flex: 15;
|
||||
}
|
||||
|
||||
.aura-config-page-subpage-container {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
background-color: rgba(255, 255, 255, 0.825);
|
||||
z-index: 6000;
|
||||
overflow-y: scroll;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-subpage-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-area.subpage-expanded
|
||||
.aura-config-page-subpage-container {
|
||||
height: calc(100% - 40px - 4rem);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-container.hide-other-operations
|
||||
.aura-config-page-operation-el:not(.preserve-operation) {
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-container.hide-other-operations
|
||||
.aura-config-page-operation-el.preserve-operation {
|
||||
flex: 0.25;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
max-width: 25%;
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
|
||||
transition: opacity 0.5s, transform 0.5s,
|
||||
max-width cubic-bezier(0, 0.42, 0.18, 1) 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-hidden {
|
||||
transform: translateY(2rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show
|
||||
.aura-config-page-operation-body {
|
||||
opacity: 1;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]
|
||||
.aura-config-page-operation-body {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show:not(.preserve-operation):hover
|
||||
.aura-config-page-operation-body {
|
||||
opacity: 0.625;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover
|
||||
.aura-config-page-operation-body {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show:not(.preserve-operation):active
|
||||
.aura-config-page-operation-body {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]::after {
|
||||
content: "别急嘛, 还在开发呢...";
|
||||
font-size: 16px;
|
||||
opacity: 0;
|
||||
color: white;
|
||||
position: absolute;
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:hover::after,
|
||||
.aura-config-page-operation-el.operation-el-show[aura-disabled="true"]:active::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el img {
|
||||
max-width: 40px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el .config-operation-title {
|
||||
color: white;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.aura-config-page-operation-el .config-operation-description {
|
||||
color: white;
|
||||
opacity: 0.75;
|
||||
font-size: small;
|
||||
}
|
||||
123
src/aura/ui/pages/config/css/status.css
Executable file
123
src/aura/ui/pages/config/css/status.css
Executable file
@@ -0,0 +1,123 @@
|
||||
.aura-config-page-status-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
align-self: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
opacity: 1;
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-status-container-hidden {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-status-main,
|
||||
.aura-config-page-status-description {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.aura-config-page-status-description {
|
||||
margin-top: 0.5rem;
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-status-description.status-description-hidden {
|
||||
transform: translateY(-2rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-status-description p {
|
||||
font-size: 18px;
|
||||
margin-left: 15px;
|
||||
margin-top: -2px;
|
||||
color: white;
|
||||
font-family: "Consolas", "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
.aura-config-page-status-description i {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.aura-config-page-central-aura-logo {
|
||||
margin: 0.5rem 3rem;
|
||||
width: 17.5%;
|
||||
}
|
||||
|
||||
.aura-config-hr-vertical {
|
||||
height: 3.75rem;
|
||||
width: 1px;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.aura-config-page-status-el {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Consolas", monospace;
|
||||
color: white;
|
||||
|
||||
/*
|
||||
.version-type {
|
||||
content: "I want to use scss plz 😇"
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
.aura-config-page-status-side {
|
||||
height: 30%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
|
||||
transition: transform 0.5s, opacity 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-status-side.left-side {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.aura-config-page-status-side.left-side.status-side-hidden {
|
||||
transform: translateX(5rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-status-side.right-side {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.aura-config-page-status-side.right-side.status-side-hidden {
|
||||
transform: translateX(-5rem);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aura-config-page-status-el .version-type {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.aura-config-page-status-el .version-content {
|
||||
font-size: 16px;
|
||||
margin-top: 5px;
|
||||
opacity: 0.625;
|
||||
}
|
||||
37
src/aura/ui/pages/config/css/toast.css
Executable file
37
src/aura/ui/pages/config/css/toast.css
Executable file
@@ -0,0 +1,37 @@
|
||||
.aura-config-page-toast-area {
|
||||
z-index: 20000;
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast {
|
||||
--bs-toast-border-width: 0 !important;
|
||||
--bs-toast-bg: #fff !important;
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast-header {
|
||||
background-color: rgb(255, 234, 202);
|
||||
|
||||
border-top-left-radius: var(--bs-toast-border-radius);
|
||||
border-top-right-radius: var(--bs-toast-border-radius);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast.acp-toast-emerg .toast-header {
|
||||
background-color: rgb(255, 202, 202);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast-header * {
|
||||
color: rgba(234, 126, 14, 0.85);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast.acp-toast-emerg .toast-header * {
|
||||
color: rgba(234, 65, 14, 0.85);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast-body p {
|
||||
margin-bottom: var(--bs-toast-padding-x);
|
||||
}
|
||||
|
||||
.aura-config-page-toast-area .toast-header .layui-icon {
|
||||
font-weight: bolder;
|
||||
margin-right: 0.5rem;
|
||||
font-size: 18px;
|
||||
}
|
||||
246
src/aura/ui/pages/configSubPages/behaviourCtrl/aikariStatus.css
Normal file
246
src/aura/ui/pages/configSubPages/behaviourCtrl/aikariStatus.css
Normal file
@@ -0,0 +1,246 @@
|
||||
.acs-behaviour-control-aikari-status-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.acs-behaviour-control-aikari-status-page p {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-aikari-description {
|
||||
margin-top: 0.5rem;
|
||||
max-width: 80%;
|
||||
opacity: 0.35;
|
||||
font-size: small;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-main-logo {
|
||||
max-width: 15%;
|
||||
opacity: 0.45;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.acs-bc-psp-operations-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.acs-bc-psp-operations-container.acs-bc-psp-oper-ctnr-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acs-bc-psp-operation-btn {
|
||||
--svg-color: rgb(17, 140, 255);
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 0.75rem;
|
||||
margin-right: 0.75rem;
|
||||
|
||||
transition: opacity 0.25s, color 0.5s;
|
||||
}
|
||||
|
||||
.acs-bc-psp-operation-btn p {
|
||||
font-size: normal;
|
||||
margin-left: 5px;
|
||||
margin-top: -1px;
|
||||
color: var(--svg-color);
|
||||
}
|
||||
|
||||
.acs-bc-psp-operation-btn svg {
|
||||
max-width: 16px;
|
||||
max-height: 16px;
|
||||
}
|
||||
|
||||
.acs-bc-psp-operation-btn.acs-bc-psp-o-btn-dangerous {
|
||||
--svg-color: rgb(244, 8, 8);
|
||||
}
|
||||
|
||||
.acs-bc-psp-operation-btn[aura-disabled="true"] {
|
||||
--svg-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.acs-bc-psp-operation-btn[aura-disabled="true"]:hover,
|
||||
.acs-bc-psp-operation-btn[aura-disabled="true"]:active {
|
||||
cursor: not-allowed !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.acs-bc-psp-operation-btn:hover,
|
||||
.acs-bc-psp-operation-btn:active {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.acs-bc-psp-operation-btn:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.acs-bc-psp-operation-btn:active {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.acs-bc-psp-download-progress-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 80%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.acs-bc-psp-download-progress-area.acs-bc-psp-dl-pbar-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acs-bc-psp-download-progress-area .progress {
|
||||
height: 2px;
|
||||
width: 80%;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.acs-bc-psp-download-progress-area .progress-bar {
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.acs-bc-psp-download-progress-area #acsBcPspDownloadPbarDesc {
|
||||
opacity: 0.6;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.acs-bc-psp-download-progress-info-area {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn {
|
||||
margin-left: 1rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn svg {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-el {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 40%;
|
||||
padding: 0.625rem 0.25rem;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-el div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area {
|
||||
flex-grow: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area-circle {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 100%;
|
||||
margin-right: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.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-aikari-status-page-status-area.pending p {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area.success
|
||||
.acs-bc-aikari-status-page-status-area-circle {
|
||||
background-color: rgb(0, 175, 38);
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area.success p {
|
||||
color: rgb(0, 150, 33);
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area.failed
|
||||
.acs-bc-aikari-status-page-status-area-circle {
|
||||
background-color: rgb(175, 0, 0);
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area.failed p {
|
||||
color: rgb(175, 0, 0);
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area.warning
|
||||
.acs-bc-aikari-status-page-status-area-circle {
|
||||
background-color: rgb(212, 127, 0);
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area.warning p {
|
||||
color: rgb(212, 127, 0);
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area.info
|
||||
.acs-bc-aikari-status-page-status-area-circle {
|
||||
background-color: #3d78ff;
|
||||
}
|
||||
|
||||
.acs-bc-aikari-status-page-status-area.info p {
|
||||
color: #3d78ff;
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast.body-display-none .toast-body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast.body-display-none .toast-header {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[closable="false"] .btn-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="success"] .toast-header {
|
||||
background-color: rgb(6, 196, 65);
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="warning"] .toast-header {
|
||||
background-color: rgb(225, 178, 44);
|
||||
color: rgb(255 255 255);
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="error"] .toast-header {
|
||||
background-color: rgb(217, 57, 4);
|
||||
color: rgb(255 255 255);
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="info"] .toast-header {
|
||||
background-color: rgb(0, 149, 222);
|
||||
color: rgb(255 255 255);
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="success"] .btn-close,
|
||||
.acs-bc-psp-toast[variant="warning"] .btn-close,
|
||||
.acs-bc-psp-toast[variant="error"] .btn-close,
|
||||
.acs-bc-psp-toast[variant="info"] .btn-close {
|
||||
color: rgb(255, 255, 255);
|
||||
filter: invert(1);
|
||||
opacity: 0.95;
|
||||
}
|
||||
214
src/aura/ui/pages/configSubPages/behaviourCtrl/aikariStatus.html
Normal file
214
src/aura/ui/pages/configSubPages/behaviourCtrl/aikariStatus.html
Normal file
@@ -0,0 +1,214 @@
|
||||
<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/aikari_color_reversed.png"
|
||||
class="acs-bc-aikari-status-page-main-logo"
|
||||
/>
|
||||
|
||||
<div class="acs-bc-psp-operations-container">
|
||||
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Refresh">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M26 18A10 10 0 1 1 16 8h6.182l-3.584 3.585L20 13l6-6l-6-6l-1.402 1.414L22.185 6H16a12 12 0 1 0 12 12Z"
|
||||
/>
|
||||
</svg>
|
||||
<p>刷新状态</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="acs-bc-psp-operation-btn"
|
||||
aura-disabled="true"
|
||||
id="acsBcPsp-operBtn-Install"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M23.5 22H23v-2h.5a4.5 4.5 0 0 0 .36-9H23l-.1-.82a7 7 0 0 0-13.88 0L9 11h-.86a4.5 4.5 0 0 0 .36 9H9v2h-.5A6.5 6.5 0 0 1 7.2 9.14a9 9 0 0 1 17.6 0A6.5 6.5 0 0 1 23.5 22"
|
||||
/>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M17 26.17V14h-2v12.17l-2.59-2.58L11 25l5 5l5-5l-1.41-1.41z"
|
||||
/>
|
||||
</svg>
|
||||
<p>下载应用</p>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-InstallSvc">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="m10 6l1.414-1.414L15 8.172V0h2v8.172l3.586-3.586L22 6l-6 6z"
|
||||
/>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M22 16a5.98 5.98 0 0 0-1.757-4.243L16 16l-4.243-4.243A6 6 0 1 0 22 16"
|
||||
/>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M30 16a13.96 13.96 0 0 0-4.105-9.895l-1.414 1.414a12 12 0 1 1-16.962 0L6.105 6.105A13.997 13.997 0 1 0 30 16"
|
||||
/>
|
||||
</svg>
|
||||
<p>安装服务</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="acs-bc-psp-operation-btn acs-bc-psp-o-btn-dangerous"
|
||||
id="acsBcPsp-operBtn-UninstallSvc"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M30 21.4L28.6 20L25 23.6L21.4 20L20 21.4l3.6 3.6l-3.6 3.6l1.4 1.4l3.6-3.6l3.6 3.6l1.4-1.4l-3.6-3.6z"
|
||||
/>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M15.4 30L5 23.8c-.6-.4-1-1-1-1.7V9.9c0-.7.4-1.4 1-1.7l10-5.9c.3-.2.6-.3 1-.3s.7.1 1 .3l10 5.9c.6.4 1 1 1 1.7V16h-2V9.9L16 4L6 9.9v12.2l10.5 6.2z"
|
||||
/>
|
||||
</svg>
|
||||
<p>卸载服务</p>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Start">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M7 28a1 1 0 0 1-1-1V5a1 1 0 0 1 1.482-.876l20 11a1 1 0 0 1 0 1.752l-20 11A1 1 0 0 1 7 28M8 6.69v18.62L24.925 16Z"
|
||||
/>
|
||||
</svg>
|
||||
<p>启动服务</p>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Stop">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M24 8v16H8V8zm0-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"
|
||||
/>
|
||||
</svg>
|
||||
<p>停止服务</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-psp-download-progress-area acs-bc-psp-dl-pbar-hidden">
|
||||
<div class="progress" role="progressbar">
|
||||
<div class="progress-bar" id="acsBcPspDownloadPbarEl"></div>
|
||||
</div>
|
||||
<div class="acs-bc-psp-download-progress-info-area">
|
||||
<p id="acsBcPspDownloadPbarDesc">等待中...</p>
|
||||
<div
|
||||
class="acs-bc-psp-operation-btn hidden"
|
||||
id="acsBcPspDownloadPbarCancelBtn"
|
||||
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.cancelDownloadTask()"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M9 10.555L10.555 9L23 21.444L21.444 23z"
|
||||
/>
|
||||
<path
|
||||
fill="var(--svg-color)"
|
||||
d="M16 2A13.914 13.914 0 0 0 2 16a13.914 13.914 0 0 0 14 14a13.914 13.914 0 0 0 14-14A13.914 13.914 0 0 0 16 2m0 26a12 12 0 1 1 12-12a12.035 12.035 0 0 1-12 12"
|
||||
/>
|
||||
</svg>
|
||||
<p>取消操作</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-aikari-status-page-status-el">
|
||||
<p>安装状态</p>
|
||||
<div
|
||||
class="acs-bc-aikari-status-page-status-area pending"
|
||||
id="acs-bc-psp-installStatus-container"
|
||||
>
|
||||
<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-aikari-status-page-status-el">
|
||||
<p>启动状态</p>
|
||||
<div
|
||||
class="acs-bc-aikari-status-page-status-area pending"
|
||||
id="acs-bc-psp-launchStatus-container"
|
||||
>
|
||||
<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-aikari-status-page-status-el">
|
||||
<p>连接状态</p>
|
||||
<div
|
||||
class="acs-bc-aikari-status-page-status-area pending"
|
||||
id="acs-bc-psp-connStatus-container"
|
||||
>
|
||||
<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-aikari-status-page-status-el" style="border-bottom: none">
|
||||
<p>版本</p>
|
||||
<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="aikariStatusNotifyToast"
|
||||
class="acs-bc-psp-toast toast"
|
||||
data-bs-autohide="false"
|
||||
>
|
||||
<div class="toast-header">
|
||||
<strong class="me-auto" id="aikariStatusNotifyToastTitle"></strong>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="toast"
|
||||
></button>
|
||||
</div>
|
||||
<div class="toast-body" id="aikariStatusNotifyToastBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
1094
src/aura/ui/pages/configSubPages/behaviourCtrl/aikariStatus.js
Normal file
1094
src/aura/ui/pages/configSubPages/behaviourCtrl/aikariStatus.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
@@ -7,36 +7,58 @@
|
||||
} = require(`${REQUIRE_BASE}/../../../../composables/settingsRenderer`);
|
||||
|
||||
const { basicSettings } = require(`${REQUIRE_BASE}/basic`);
|
||||
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 initDeviceInfoPostSettingsPage = () => {
|
||||
const deviceInfoPostSubPageEl = document.getElementById(
|
||||
"device-info-post-config-subpage"
|
||||
);
|
||||
settingsRenderer(deviceInfoPostSubPageEl, deviceInfoPostSettings);
|
||||
};
|
||||
|
||||
const renderSubPages = async () => {
|
||||
await updatePlsSettingsFromLocal();
|
||||
await updatePlsRulesFromLocal();
|
||||
await updateAikariSettingsFromLocal();
|
||||
await updateAikariRulesFromLocal();
|
||||
|
||||
initBasicSettingsPage();
|
||||
initDeviceInfoPostSettingsPage();
|
||||
};
|
||||
|
||||
const onMounted = () => {
|
||||
const rootEl = document.getElementById("acs-behaviour-control-el");
|
||||
preInitUIReactives();
|
||||
initStatusPage();
|
||||
renderSubPages();
|
||||
setTimeout(() => {
|
||||
rootEl.classList.remove("acs-behaviour-control-hidden");
|
||||
renderSubPages();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
.acs-behaviour-control-pls-status-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.acs-behaviour-control-pls-status-page p {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-pls-description {
|
||||
margin-top: 0.5rem;
|
||||
max-width: 80%;
|
||||
opacity: 0.35;
|
||||
font-size: small;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-main-logo {
|
||||
max-width: 13.5%;
|
||||
opacity: 0.45;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-el {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 40%;
|
||||
padding: 0.625rem 0.25rem;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-el div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area {
|
||||
flex-grow: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area-circle {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 100%;
|
||||
margin-right: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.pending
|
||||
.acs-bc-pls-status-page-status-area-circle {
|
||||
background-color: rgba(0, 0, 0, 0.375);
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.pending p {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.success
|
||||
.acs-bc-pls-status-page-status-area-circle {
|
||||
background-color: rgb(0, 175, 38);
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.success p {
|
||||
color: rgb(0, 150, 33);
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.failed
|
||||
.acs-bc-pls-status-page-status-area-circle {
|
||||
background-color: rgb(175, 0, 0);
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.failed p {
|
||||
color: rgb(175, 0, 0);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<div class="acs-behaviour-control-pls-status-page">
|
||||
<p class="acs-bc-pls-status-page-pls-description">
|
||||
HugoAura ProxyLayerServices (Aura-PLS) 是基于 Python + MITMProxy 实现的代理服务,
|
||||
用于解密并修改希沃基础服务 (SeewoCore) 的 MQTT 数据包,
|
||||
实现行为监控、伪造上报等功能
|
||||
</p>
|
||||
<img
|
||||
src="../../aura/ui/static/aura_pls.png"
|
||||
class="acs-bc-pls-status-page-main-logo"
|
||||
/>
|
||||
|
||||
<div class="acs-bc-pls-status-page-status-el">
|
||||
<p>安装状态</p>
|
||||
<div
|
||||
class="acs-bc-pls-status-page-status-area pending"
|
||||
id="acs-bc-psp-installStatus-container"
|
||||
>
|
||||
<span class="acs-bc-pls-status-page-status-area-circle"></span>
|
||||
<p id="acs-bc-psp-installStatus-text">未安装</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-pls-status-page-status-el">
|
||||
<p>启动状态</p>
|
||||
<div
|
||||
class="acs-bc-pls-status-page-status-area pending"
|
||||
id="acs-bc-psp-launchStatus-container"
|
||||
>
|
||||
<span class="acs-bc-pls-status-page-status-area-circle"></span>
|
||||
<p id="acs-bc-psp-launchStatus-text">未启动</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-pls-status-page-status-el">
|
||||
<p>连接状态</p>
|
||||
<div
|
||||
class="acs-bc-pls-status-page-status-area pending"
|
||||
id="acs-bc-psp-connStatus-container"
|
||||
>
|
||||
<span class="acs-bc-pls-status-page-status-area-circle"></span>
|
||||
<p id="acs-bc-psp-connStatus-text">已断开</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-pls-status-page-status-el" style="border-bottom: none">
|
||||
<p>版本</p>
|
||||
<div class="acs-bc-pls-status-page-status-area">
|
||||
<p id="acs-bc-psp-version-text">不可用</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,86 +0,0 @@
|
||||
(() => {
|
||||
const REQUIRE_BASE = "../../aura/ui/pages/configSubPages/behaviourCtrl";
|
||||
const IPC_METHOD_BASE = "$aura.pls";
|
||||
|
||||
const {
|
||||
updatePlsStatusFromLocal,
|
||||
} = require(`${REQUIRE_BASE}/../../../composables/plsConfigManager`);
|
||||
|
||||
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;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
areaContainerText.textContent = text ? text : "";
|
||||
return true;
|
||||
};
|
||||
|
||||
const updateStatus = async () => {
|
||||
const curPlsStats = await updatePlsStatusFromLocal();
|
||||
|
||||
const acIdInst = "acs-bc-psp-installStatus-container";
|
||||
const atIdInst = "acs-bc-psp-installStatus-text";
|
||||
switch (curPlsStats.installed) {
|
||||
case true:
|
||||
updateStatusEl(acIdInst, atIdInst, "SUCCESS", "已安装");
|
||||
break;
|
||||
case false:
|
||||
updateStatusEl(acIdInst, atIdInst, "PENDING", "未安装");
|
||||
}
|
||||
|
||||
const acIdLaunch = "acs-bc-psp-launchStatus-container";
|
||||
const atIdLaunch = "acs-bc-psp-launchStatus-text";
|
||||
switch (curPlsStats.launched) {
|
||||
case true:
|
||||
updateStatusEl(acIdLaunch, atIdLaunch, "SUCCESS", "已启动");
|
||||
break;
|
||||
case false:
|
||||
updateStatusEl(acIdLaunch, atIdLaunch, "PENDING", "未启动");
|
||||
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:
|
||||
updateStatusEl(acIdConn, atIdConn, "FAILED", "连接失败");
|
||||
break;
|
||||
}
|
||||
|
||||
if (curPlsStats.version && curPlsStats.version !== "未知") {
|
||||
const versionTextEl = document.getElementById("acs-bc-psp-version-text");
|
||||
versionTextEl.textContent = "v" + curPlsStats.version;
|
||||
}
|
||||
};
|
||||
|
||||
const onMounted = () => {
|
||||
updateStatus();
|
||||
};
|
||||
|
||||
onMounted();
|
||||
})();
|
||||
@@ -1,8 +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`);
|
||||
updateAikariConfigToRemote,
|
||||
updateAikariTelemetryConfigToRemote,
|
||||
} = require(`${REQUIRE_BASE}/../../../../composables/aikariConfigManager`);
|
||||
|
||||
const basicSettings = [
|
||||
{
|
||||
@@ -11,33 +16,202 @@ const basicSettings = [
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
id: "authToken",
|
||||
id: "aikarWsPreferPort",
|
||||
type: "input",
|
||||
subType: "text",
|
||||
name: "WebSocket 认证密钥",
|
||||
description: "选择一个安全的密钥, 用于 PLS 侧验证 Aura 前端身份",
|
||||
restart: true,
|
||||
subType: "number",
|
||||
name: "Aikari WS 默认监听端口",
|
||||
description: "Aikari WebSocket 服务器默认监听的端口",
|
||||
reactive: true,
|
||||
reactiveVal: ["root.settings"],
|
||||
restart: false,
|
||||
reload: false,
|
||||
restartPLS: false,
|
||||
aikariRequired: true,
|
||||
restartAikari: false,
|
||||
warning: true,
|
||||
warningContent: "Aikari 仍会在默认端口被占用时, 自动随机端口重试",
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: "",
|
||||
placeHolder: "输入一个密钥",
|
||||
placeHolder: "输入端口号 (10000 ~ 65535)",
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.plsToken;
|
||||
if (!global.__HUGO_AURA__.aikariSettings) return "";
|
||||
return global.__HUGO_AURA__.aikariSettings.wsPreferPort;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (newVal === "" || !newVal)
|
||||
return { valid: false, hint: "请输入认证密钥" };
|
||||
return { valid: false, hint: "请输入端口号" };
|
||||
|
||||
if (newVal.length < 8) {
|
||||
return { valid: false, hint: "至少输入 8 位字符" };
|
||||
const numberNewVal = Number(newVal);
|
||||
if (
|
||||
numberNewVal === NaN ||
|
||||
!(10000 <= numberNewVal) ||
|
||||
!(newVal <= 65535)
|
||||
) {
|
||||
return { valid: false, hint: "请输入合法的端口号 (10000 ~ 65535)" };
|
||||
}
|
||||
|
||||
global.__HUGO_AURA_CONFIG__.plsToken = newVal;
|
||||
global.__HUGO_AURA__.aikariSettings.wsPreferPort = numberNewVal;
|
||||
updateAikariConfigToRemote("wsPreferPort", numberNewVal);
|
||||
return { valid: true };
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
id: "aikariForceRegenWsTlsCert",
|
||||
type: "switch",
|
||||
name: "重新生成 WS TLS 证书",
|
||||
description: "Aikari 将在下次启动时重新生成用于 WebSocket 的 TLS 证书",
|
||||
reactive: true,
|
||||
reactiveVal: ["root.settings"],
|
||||
restart: false,
|
||||
reload: false,
|
||||
aikariRequired: true,
|
||||
restartAikari: true,
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
if (!global.__HUGO_AURA__.aikariSettings) return "";
|
||||
return global.__HUGO_AURA__.aikariSettings.tls.regenWsCertNextLaunch;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (typeof newVal !== "boolean") return false;
|
||||
|
||||
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 = "复制失败";
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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 };
|
||||
@@ -0,0 +1,104 @@
|
||||
.acs-bc-dsc-fop-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-please-wait,
|
||||
.acs-bc-dsc-fop-on-req-error,
|
||||
.acs-bc-dsc-fop-on-not-bind {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-please-wait svg,
|
||||
.acs-bc-dsc-fop-on-req-error svg,
|
||||
.acs-bc-dsc-fop-on-not-bind svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-please-wait p,
|
||||
.acs-bc-dsc-fop-on-req-error p,
|
||||
.acs-bc-dsc-fop-on-not-bind p {
|
||||
margin-left: 0.5rem;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-please-wait[auraIf="false"],
|
||||
.acs-bc-dsc-fop-on-req-error[auraIf="false"],
|
||||
.acs-bc-dsc-fop-on-not-bind[auraIf="false"],
|
||||
.acs-bc-dsc-fop-main[auraIf="false"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-main .disks-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-disk-el {
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.25);
|
||||
margin-left: 0.375rem;
|
||||
margin-right: 0.375rem;
|
||||
|
||||
transition: all 0.5s;
|
||||
/* 没有用, 因为元素全被重新创建了 */
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-disk-el.active {
|
||||
background-color: #1d70f2;
|
||||
color: white;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-disk-el.active:hover {
|
||||
background-color: #4e8df2;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-main-hint-area {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: -0.25rem;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-main-hint-area svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-main-hint-area div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-main-hint-area p {
|
||||
margin-top: -1px;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-main-hint-area .acs-bc-dsc-fop-main-hint-freeze {
|
||||
margin-right: 0.5rem;
|
||||
color: #1d70f2;
|
||||
}
|
||||
|
||||
.acs-bc-dsc-fop-main-hint-area .acs-bc-dsc-fop-main-hint-unfreeze {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<div class="acs-bc-dsc-fop-container">
|
||||
<div class="acs-bc-dsc-fop-please-wait" auraIf="true">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M16 30a14 14 0 1 1 14-14a14 14 0 0 1-14 14m0-26a12 12 0 1 0 12 12A12 12 0 0 0 16 4"
|
||||
/>
|
||||
<path fill="currentColor" d="M20.59 22L15 16.41V7h2v8.58l5 5.01z" />
|
||||
</svg>
|
||||
|
||||
<p>无数据可用, 检查 Aikari 连接</p>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-dsc-fop-on-req-error" auraIf="false">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path fill="currentColor" d="M9 10.555L10.555 9L23 21.444L21.444 23z" />
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M16 2A13.914 13.914 0 0 0 2 16a13.914 13.914 0 0 0 14 14a13.914 13.914 0 0 0 14-14A13.914 13.914 0 0 0 16 2m0 26a12 12 0 1 1 12-12a12.035 12.035 0 0 1-12 12"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<p>获取磁盘信息失败: <font id="acsBcDscFopOnReqErrorDetail"></font></p>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-dsc-fop-on-not-bind" auraIf="false">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M31.324 11.261A14.27 14.27 0 0 0 22.25 8H22v2h.25c2.608 0 5.155.837 7.246 2.372a12.18 12.18 0 0 1-7.548 7.036q.052-.605.052-1.22C22 10.366 15.635 4 7.812 4c-.929 0-1.856.09-2.757.268l-.657.13l-.13.657A14.3 14.3 0 0 0 4 7.812c0 4.124 1.78 7.831 4.598 10.426A14.2 14.2 0 0 0 8 22.254l.001.001a14.27 14.27 0 0 0 3.261 9.07l.439.53l.652-.22a14.18 14.18 0 0 0 9.237-10.046a14.18 14.18 0 0 0 10.045-9.237l.22-.652zM12.372 29.496A12.27 12.27 0 0 1 10 22.251c0-.912.113-1.796.303-2.652a14.1 14.1 0 0 0 9.105 2.349a12.18 12.18 0 0 1-7.036 7.548m7.51-9.613q-.833.116-1.694.117c-2.715 0-5.218-.904-7.247-2.412a12.4 12.4 0 0 1 4.048-5.204l-1.28-1.53A14.3 14.3 0 0 0 9.37 16.2A12.14 12.14 0 0 1 6.117 6.117A12 12 0 0 1 7.812 6C14.532 6 20 11.468 20 18.188q0 .862-.117 1.695Z"
|
||||
/>
|
||||
<circle cx="20" cy="2" r="2" fill="currentColor" />
|
||||
<circle cx="27" cy="26" r="2" fill="currentColor" />
|
||||
<circle cx="2" cy="20" r="2" fill="currentColor" />
|
||||
</svg>
|
||||
|
||||
<p>当前设备暂未绑定学校, 无法查询冰点信息</p>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-dsc-fop-main" auraIf="false">
|
||||
<div class="disks-container">
|
||||
<p class="acs-bc-dsc-fop-disk-el">C 盘</p>
|
||||
</div>
|
||||
|
||||
<div class="acs-bc-dsc-fop-main-hint-area">
|
||||
<div class="acs-bc-dsc-fop-main-hint-freeze">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M21.415 12H28v-2h-4.585L28 5.415L26.586 4L22 8.587V4h-2v6.587L18.587 12H17V8h-2v4h-1.587L12 10.587V4h-2v4.587L5.414 4L4 5.415L8.585 10H4v2h6.585L12 13.415V15H8v2h4v1.587L10.587 20H4v2h4.587L4 26.586l1.414 1.415L10 23.415V28h2v-6.585L13.415 20H15v4h2v-4h1.585L20 21.415V28h2v-4.585l4.585 4.586L28 26.586L23.413 22H28v-2h-6.587L20 18.587V17h4v-2h-4v-1.585ZM18 18h-4v-4h4Z"
|
||||
/>
|
||||
</svg>
|
||||
<p>已冻结</p>
|
||||
</div>
|
||||
<div class="acs-bc-dsc-fop-main-hint-unfreeze">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M16 12.005a4 4 0 1 1-4 4a4.005 4.005 0 0 1 4-4m0-2a6 6 0 1 0 6 6a6 6 0 0 0-6-6M5.394 6.813L6.81 5.399l3.505 3.506L8.9 10.319zM2 15.005h5v2H2zm3.394 10.193L8.9 21.692l1.414 1.414l-3.505 3.506zM15 25.005h2v5h-2zm6.687-1.9l1.414-1.414l3.506 3.506l-1.414 1.414zm3.313-8.1h5v2h-5zm-3.313-6.101l3.506-3.506l1.414 1.414l-3.506 3.506zM15 2.005h2v5h-2z"
|
||||
/>
|
||||
</svg>
|
||||
<p>未冻结</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,137 @@
|
||||
// @ts-check
|
||||
|
||||
(() => {
|
||||
const REQUIRE_BASE = "../..";
|
||||
const { genRandomHex } = require(`${REQUIRE_BASE}/aura/utils/crypto`);
|
||||
|
||||
const composables = {
|
||||
getAndUpdateDiskInfo: async (curConfig) => {
|
||||
const progressingEl = document.getElementsByClassName(
|
||||
"acs-bc-dsc-fop-please-wait"
|
||||
)[0];
|
||||
const onErrorEl = document.getElementsByClassName(
|
||||
"acs-bc-dsc-fop-on-req-error"
|
||||
)[0];
|
||||
const onNotBindEl = document.getElementsByClassName(
|
||||
"acs-bc-dsc-fop-on-not-bind"
|
||||
)[0];
|
||||
const mainEl = document.getElementsByClassName("acs-bc-dsc-fop-main")[0];
|
||||
const diskContainerEl =
|
||||
document.getElementsByClassName("disks-container")[0];
|
||||
|
||||
const seewoProxyPort = window._ACCEPT_DATA.data.ports.SeewoProxyHTTP;
|
||||
|
||||
const reqPromise = new Promise((resolve) => {
|
||||
fetch(
|
||||
`https://127.0.0.1:${seewoProxyPort}/forward/freeze/api/v1/get_disk_data`,
|
||||
{
|
||||
headers: {
|
||||
accept: "application/json, text/plain, */*",
|
||||
"X-Auth-Traceid": genRandomHex(),
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(async (response) => {
|
||||
if (response.status !== 200) {
|
||||
resolve({
|
||||
success: true,
|
||||
data: null,
|
||||
status: response.status,
|
||||
});
|
||||
}
|
||||
|
||||
const parsedData = await response.json();
|
||||
|
||||
resolve({
|
||||
success: true,
|
||||
data: parsedData,
|
||||
status: response.status,
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
resolve({ success: false, data: null, errorObj: e });
|
||||
});
|
||||
});
|
||||
|
||||
const responseInfo = await reqPromise;
|
||||
|
||||
progressingEl.setAttribute("auraIf", "false");
|
||||
|
||||
if (!responseInfo.success) {
|
||||
onNotBindEl.setAttribute("auraIf", "false");
|
||||
mainEl.setAttribute("auraIf", "false");
|
||||
onErrorEl.setAttribute("auraIf", "true");
|
||||
const detailEl = document.getElementById("acsBcDscFopOnReqErrorDetail");
|
||||
// @ts-expect-error
|
||||
detailEl.textContent = responseInfo.errorObj;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (responseInfo.status !== 200) {
|
||||
onErrorEl.setAttribute("auraIf", "false");
|
||||
mainEl.setAttribute("auraIf", "false");
|
||||
onNotBindEl.setAttribute("auraIf", "true");
|
||||
return;
|
||||
}
|
||||
|
||||
diskContainerEl.innerHTML = ``;
|
||||
|
||||
const curDisks = [];
|
||||
for (const disk of responseInfo.data.data[0].disksData) {
|
||||
curDisks.push({ name: disk.diskName, status: disk.protectedStatus });
|
||||
}
|
||||
|
||||
const diskElTemplate = document.createElement("p");
|
||||
diskElTemplate.classList.add("acs-bc-dsc-fop-disk-el");
|
||||
if (!curConfig.enabled) {
|
||||
for (const disk of curDisks) {
|
||||
const curDiskEl = diskElTemplate.cloneNode();
|
||||
if (disk.status !== 0) {
|
||||
// @ts-expect-error
|
||||
curDiskEl.classList.add("active");
|
||||
}
|
||||
curDiskEl.textContent = `${disk.name.toUpperCase()} 盘`;
|
||||
diskContainerEl.appendChild(curDiskEl);
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
onErrorEl.setAttribute("auraIf", "false");
|
||||
onNotBindEl.setAttribute("auraIf", "false");
|
||||
mainEl.setAttribute("auraIf", "true");
|
||||
},
|
||||
};
|
||||
|
||||
const onMounted = () => {
|
||||
const rootEl = document.getElementsByClassName(
|
||||
"acs-bc-dsc-fop-container"
|
||||
)[0];
|
||||
|
||||
const eventListener = (_event) => {
|
||||
if (!global.__HUGO_AURA__.aikariRules) return;
|
||||
composables.getAndUpdateDiskInfo(
|
||||
global.__HUGO_AURA__.aikariRules.ssaFeatures.securityPolicies
|
||||
.freezeManagement.freezeDiskInfoPost
|
||||
);
|
||||
};
|
||||
rootEl.addEventListener("onAssociateValueUpdated", eventListener);
|
||||
|
||||
setTimeout(() => {
|
||||
eventListener();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
onMounted();
|
||||
})();
|
||||
@@ -6,20 +6,6 @@
|
||||
<li class="nav-item" role="presentation">
|
||||
<button
|
||||
class="nav-link active"
|
||||
id="aura-subpage-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#aura-subpage"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="aura-subpage"
|
||||
aria-selected="true"
|
||||
>
|
||||
Aura 设置
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button
|
||||
class="nav-link"
|
||||
id="auth-subpage-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#auth-subpage"
|
||||
@@ -45,16 +31,24 @@
|
||||
上报屏蔽
|
||||
</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
|
||||
class="tab-pane fade show active"
|
||||
id="aura-subpage"
|
||||
role="tabpanel"
|
||||
aria-labelledby="aura-subpage-tab"
|
||||
></div>
|
||||
<div
|
||||
class="tab-pane fade show"
|
||||
id="auth-subpage"
|
||||
role="tabpanel"
|
||||
aria-labelledby="auth-subpage-tab"
|
||||
@@ -65,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>
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
const {
|
||||
settingsRenderer,
|
||||
} = require("../../aura/ui/composables/settingsRenderer");
|
||||
const { auraSettings } = require(`${pathBase}/aura`);
|
||||
const { authSettings } = require(`${pathBase}/auth`);
|
||||
const { banAuditSettings } = require(`${pathBase}/audit`);
|
||||
|
||||
const initAuraSubPage = () => {
|
||||
const auraSettingsSubPageEl = document.getElementById("aura-subpage");
|
||||
settingsRenderer(auraSettingsSubPageEl, auraSettings);
|
||||
};
|
||||
const { uxAndAppearanceSettings } = require(`${pathBase}/uxAppearance`);
|
||||
|
||||
const initAuthSubPage = () => {
|
||||
const authSubPageEl = document.getElementById("auth-subpage");
|
||||
@@ -24,10 +19,17 @@
|
||||
settingsRenderer(banAuditSubPageEl, banAuditSettings);
|
||||
};
|
||||
|
||||
const initUxAndAppearanceSubPage = () => {
|
||||
const uxAndAppearancePageEl = document.getElementById(
|
||||
"appearance-ctl-subpage"
|
||||
);
|
||||
settingsRenderer(uxAndAppearancePageEl, uxAndAppearanceSettings);
|
||||
};
|
||||
|
||||
const onMounted = () => {
|
||||
initAuraSubPage();
|
||||
initAuthSubPage();
|
||||
initBanAuditSubPage();
|
||||
initUxAndAppearanceSubPage();
|
||||
|
||||
const rootEl = document.getElementById("acs-disable-limit-root-el");
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -8,7 +8,8 @@ const banAuditSettings = [
|
||||
id: "disableFridayReport",
|
||||
type: "switch",
|
||||
name: "禁用 Friday 错误统计",
|
||||
description: "重置 CVTE 的 Friday 错误收集服务载入 URL, 避免意外的信息上传",
|
||||
description:
|
||||
"重置 CVTE 的 Friday 错误收集服务载入 URL, 避免意外的信息上传",
|
||||
restart: true,
|
||||
reload: false,
|
||||
associateVal: null,
|
||||
@@ -39,7 +40,8 @@ const banAuditSettings = [
|
||||
restart: true,
|
||||
reload: false,
|
||||
tip: true,
|
||||
tipTitle: '启用后, 可能造成部分操作出现较长延迟 (如冰点操作)。希沃管家会尝试五次上报, 均失败后才会进行下一步操作',
|
||||
tipTitle:
|
||||
"启用后, 可能造成部分操作出现较长延迟 (如冰点操作)。希沃管家会尝试五次上报, 均失败后才会进行下一步操作",
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: false,
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
const auraSettings = [
|
||||
{
|
||||
id: 0,
|
||||
categoryName: "安全性",
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
id: "enableAuraSettingsPasswd",
|
||||
type: "switch",
|
||||
name: "启用访问密码",
|
||||
description: "启用后, Aura 设置 UI 需要输入密码才可访问",
|
||||
restart: false,
|
||||
reload: false,
|
||||
tip: true,
|
||||
tipTitle: "在 0.1.1-beta 版本发布后, 启用访问密码将加密配置文件",
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings
|
||||
.settingsPasswordEnabled;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordEnabled =
|
||||
newVal;
|
||||
// TODO: Trigger enc config
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
id: "auraSettingsPasswd",
|
||||
type: "input",
|
||||
subType: "password",
|
||||
name: "访问密码",
|
||||
description: "此密码将用于访问 Aura 设置 UI",
|
||||
restart: false,
|
||||
reload: false,
|
||||
associateVal: ["auraSettings.settingsPasswordEnabled"],
|
||||
auraIf: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings
|
||||
.settingsPasswordEnabled;
|
||||
},
|
||||
defaultValue: "",
|
||||
placeHolder: "留空表示不修改, 保留已设置值",
|
||||
valueGetter: () => {
|
||||
return "";
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (newVal === "" || !newVal) return { valid: true };
|
||||
if (newVal.length < 8)
|
||||
return { valid: false, hint: "请输入至少 8 位密码" };
|
||||
|
||||
const hasNumber = /[0-9]/.test(newVal);
|
||||
const hasLetter = /[a-zA-Z]/.test(newVal);
|
||||
const hasSpecial = /[^a-zA-Z0-9]/.test(newVal);
|
||||
|
||||
const typeCount = [hasNumber, hasLetter, hasSpecial].filter(
|
||||
Boolean
|
||||
).length;
|
||||
|
||||
if (typeCount < 2) {
|
||||
return {
|
||||
valid: false,
|
||||
hint: "请包含数字 / 字母 / 特殊字符中的至少 2 种",
|
||||
};
|
||||
}
|
||||
|
||||
const crypto = require("crypto");
|
||||
const result = crypto
|
||||
.createHash("sha512")
|
||||
.update(newVal + "EndlessX")
|
||||
.digest("hex")
|
||||
.toUpperCase();
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordWithSalt =
|
||||
result;
|
||||
return { valid: true };
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
categoryName: "外观",
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
id: "enablePasswdDialogBlur",
|
||||
type: "switch",
|
||||
name: "密码验证框毛玻璃效果",
|
||||
description: "启用后, 密码验证时, 背景将具有毛玻璃效果",
|
||||
restart: false,
|
||||
reload: false,
|
||||
tip: true,
|
||||
tipTitle: "不建议在较旧 (如 i5 8 代) 机型上开启, 可能导致性能问题",
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: true,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings.appearance
|
||||
.enablePasswdDialogBlur;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.appearance.enablePasswdDialogBlur =
|
||||
newVal;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = { auraSettings };
|
||||
@@ -131,6 +131,253 @@ const authSettings = [
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
categoryName: "屏幕锁",
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
id: "enableScreenLockOverride",
|
||||
type: "switch",
|
||||
name: "启用屏幕锁覆写功能",
|
||||
description: "覆写希沃管家的屏幕锁组件, 绕过限制",
|
||||
restart: false,
|
||||
reload: false,
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: true,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
|
||||
.enabled;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].enabled =
|
||||
newVal;
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
id: "disableKeyboardHook",
|
||||
type: "switch",
|
||||
name: "允许快捷键操作",
|
||||
description: "屏蔽键盘 DLL Hook, 允许在屏幕锁中操作快捷键",
|
||||
restart: false,
|
||||
reload: false,
|
||||
tip: true,
|
||||
tipTitle: "此功能正在测试中, 可能并不稳定",
|
||||
associateVal: ["rewrite.vendor/screenLock.enabled"],
|
||||
auraIf: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
|
||||
.enabled;
|
||||
},
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
|
||||
.disableKeyboardHook;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.rewrite[
|
||||
"vendor/screenLock"
|
||||
].disableKeyboardHook = newVal;
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
id: "fastfailScreenLock",
|
||||
type: "switch",
|
||||
name: "禁用屏幕锁",
|
||||
description: "启用本功能后, 屏幕锁将完全无法使用, <b>请注意风险</b>",
|
||||
restart: false,
|
||||
reload: false,
|
||||
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: ["自定义激活码", "不修改"],
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
|
||||
.authRewriteType;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
global.__HUGO_AURA_CONFIG__.rewrite[
|
||||
"vendor/screenLock"
|
||||
].authRewriteType = newVal;
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 6,
|
||||
id: "customActivationCode",
|
||||
type: "input",
|
||||
subType: "password",
|
||||
name: "自定义激活码",
|
||||
description: '请在屏幕锁页面下方选择 "激活码解锁" 以使用',
|
||||
restart: false,
|
||||
reload: false,
|
||||
warning: true,
|
||||
warningContent: "密码为 6 位纯数字",
|
||||
associateVal: [
|
||||
"rewrite.vendor/screenLock.enabled",
|
||||
"rewrite.vendor/screenLock.authRewriteType",
|
||||
"rewrite.vendor/screenLock.fastfail",
|
||||
],
|
||||
auraIf: () => {
|
||||
return (
|
||||
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].enabled &&
|
||||
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"]
|
||||
.authRewriteType === "customActivationCode"
|
||||
);
|
||||
},
|
||||
auraDisable: () => {
|
||||
if (
|
||||
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"].fastfail
|
||||
) {
|
||||
return { value: true, tooltip: '关闭 "禁用屏幕锁" 以继续' };
|
||||
}
|
||||
return { value: false };
|
||||
},
|
||||
defaultValue: "",
|
||||
placeHolder: "留空表示不修改, 保留已设置值",
|
||||
valueGetter: () => {
|
||||
return "";
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (newVal === "" || !newVal) return { valid: true };
|
||||
if (newVal.length !== 6)
|
||||
return { valid: false, hint: "仅可输入 6 位密码" };
|
||||
if (!/^\d+$/.test(newVal)) {
|
||||
return { valid: false, hint: "仅允许纯数字密码" };
|
||||
}
|
||||
const __config =
|
||||
global.__HUGO_AURA_CONFIG__.rewrite["vendor/screenLock"];
|
||||
const crypto = require("crypto");
|
||||
const result = crypto
|
||||
.createHash("md5")
|
||||
.update(newVal + "auraScreenLockCrack")
|
||||
.digest("hex");
|
||||
__config.customActivationCode.activationCodeWithSalt = result;
|
||||
return { valid: true };
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
categoryName: "基础设施",
|
||||
child: [
|
||||
{
|
||||
@@ -143,6 +390,9 @@ const authSettings = [
|
||||
reload: false,
|
||||
tip: true,
|
||||
tipTitle: "启用后, 按下 Ctrl + Shift + I 即可打开 DevTools",
|
||||
warning: true,
|
||||
warningContent:
|
||||
"在操作不当的情况下, 有可能造成 DevTools 永久无法激活 (Electron 的 Bug), 此时请使用 Chrome 远程调试",
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: false,
|
||||
|
||||
@@ -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 };
|
||||
8
src/aura/ui/pages/configSubPages/preferences/preferences.css
Executable file
8
src/aura/ui/pages/configSubPages/preferences/preferences.css
Executable file
@@ -0,0 +1,8 @@
|
||||
.aura-config-subpage-preferences-root {
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-subpage-preferences-root.acs-preferences-root-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
69
src/aura/ui/pages/configSubPages/preferences/preferences.html
Executable file
69
src/aura/ui/pages/configSubPages/preferences/preferences.html
Executable file
@@ -0,0 +1,69 @@
|
||||
<div
|
||||
id="acs-preferences-root-el"
|
||||
class="aura-config-subpage-preferences-root acs-preferences-root-hidden"
|
||||
>
|
||||
<ul class="nav nav-underline mb-3" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button
|
||||
class="nav-link active"
|
||||
id="aura-subpage-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#aura-subpage"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="aura-subpage"
|
||||
aria-selected="true"
|
||||
>
|
||||
Aura 设置
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button
|
||||
class="nav-link"
|
||||
id="debug-subpage-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#debug-subpage"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="debug-subpage"
|
||||
aria-selected="false"
|
||||
>
|
||||
调试选项
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button
|
||||
class="nav-link"
|
||||
id="about-subpage-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#about-subpage"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="about-subpage"
|
||||
aria-selected="false"
|
||||
>
|
||||
关于项目
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div
|
||||
class="tab-pane fade show active"
|
||||
id="aura-subpage"
|
||||
role="tabpanel"
|
||||
aria-labelledby="aura-subpage-tab"
|
||||
></div>
|
||||
<div
|
||||
class="tab-pane fade"
|
||||
id="debug-subpage"
|
||||
role="tabpanel"
|
||||
aria-labelledby="debug-subpage-tab"
|
||||
></div>
|
||||
<div
|
||||
class="tab-pane fade"
|
||||
id="about-subpage"
|
||||
role="tabpanel"
|
||||
aria-labelledby="about-subpage-tab"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
31
src/aura/ui/pages/configSubPages/preferences/preferences.js
Executable file
31
src/aura/ui/pages/configSubPages/preferences/preferences.js
Executable file
@@ -0,0 +1,31 @@
|
||||
(() => {
|
||||
const pathBase = "../../aura/ui/pages/configSubPages/preferences/settings";
|
||||
|
||||
const {
|
||||
settingsRenderer,
|
||||
} = require("../../aura/ui/composables/settingsRenderer");
|
||||
const { auraSettings } = require(`${pathBase}/aura`);
|
||||
const { debugSettings } = require(`${pathBase}/debug`);
|
||||
|
||||
const initAuraSubPage = () => {
|
||||
const auraSettingsSubPageEl = document.getElementById("aura-subpage");
|
||||
settingsRenderer(auraSettingsSubPageEl, auraSettings);
|
||||
};
|
||||
|
||||
const initDebugSubPage = () => {
|
||||
const debugSubPageEl = document.getElementById("debug-subpage");
|
||||
settingsRenderer(debugSubPageEl, debugSettings);
|
||||
};
|
||||
|
||||
const onMounted = () => {
|
||||
initAuraSubPage();
|
||||
initDebugSubPage();
|
||||
|
||||
const rootEl = document.getElementById("acs-preferences-root-el");
|
||||
setTimeout(() => {
|
||||
rootEl.classList.remove("acs-preferences-root-hidden");
|
||||
}, 500);
|
||||
};
|
||||
|
||||
onMounted();
|
||||
})();
|
||||
479
src/aura/ui/pages/configSubPages/preferences/settings/aura.js
Executable file
479
src/aura/ui/pages/configSubPages/preferences/settings/aura.js
Executable file
@@ -0,0 +1,479 @@
|
||||
// @ts-check
|
||||
|
||||
const functions = {
|
||||
/**
|
||||
*
|
||||
* @param {"enc" | "update"} mode
|
||||
* @param {SHA256EncryptedPassword | null} password
|
||||
*/
|
||||
handleEnableConfigEncryption: async (mode, password) => {
|
||||
let exiPassword = "";
|
||||
if (!password) {
|
||||
exiPassword =
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordWithSalt;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case "enc":
|
||||
ipcRenderer.invoke("$aura.config.setConfigEncSettings", {
|
||||
target: true,
|
||||
});
|
||||
global.ipcRenderer.invoke("$aura.config.dispatchConfigFromRenderer", {
|
||||
data: JSON.stringify(global.__HUGO_AURA_CONFIG__),
|
||||
});
|
||||
global.__HUGO_AURA_CONFIG_MGR__.encryptConfig(
|
||||
global.__HUGO_AURA_CONFIG__,
|
||||
password ? password : exiPassword
|
||||
);
|
||||
global.__HUGO_AURA_CONFIG_MGR__.saveEncPassword(
|
||||
password ? password : exiPassword
|
||||
);
|
||||
break;
|
||||
case "update":
|
||||
const result = await global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
|
||||
global.__HUGO_AURA_CONFIG__,
|
||||
null
|
||||
);
|
||||
if (result.success) {
|
||||
ipcRenderer.invoke("$aura.config.setConfigEncSettings", {
|
||||
target: true,
|
||||
});
|
||||
global.ipcRenderer.invoke("$aura.config.dispatchConfigFromRenderer", {
|
||||
data: JSON.stringify(global.__HUGO_AURA_CONFIG__),
|
||||
});
|
||||
global.__HUGO_AURA_CONFIG_MGR__.encryptConfig(
|
||||
global.__HUGO_AURA_CONFIG__,
|
||||
password ? password : exiPassword
|
||||
);
|
||||
global.__HUGO_AURA_CONFIG_MGR__.saveEncPassword(
|
||||
password ? password : exiPassword
|
||||
);
|
||||
} else {
|
||||
// TODO: Error handling
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SHA256EncryptedPassword} password
|
||||
*/
|
||||
handle2ndPasswordPrompt: async (password) => {
|
||||
const acsDialogAreaEl = document.getElementsByClassName(
|
||||
"aura-config-page-auth-dialog-area"
|
||||
)[0];
|
||||
const acpAppBarEl = document.getElementsByClassName(
|
||||
"aura-config-page-header-area"
|
||||
)[0];
|
||||
const acpDialogTitleEl = document.getElementsByClassName(
|
||||
"acp-auth-dialog-title"
|
||||
)[0];
|
||||
const acpDialogConfirmBtnEl = document.getElementsByClassName(
|
||||
"acp-auth-confirm-btn"
|
||||
)[0];
|
||||
const acpDialogCancelBtnEl = document.getElementsByClassName(
|
||||
"acp-auth-cancel-btn"
|
||||
)[0];
|
||||
// @ts-expect-error
|
||||
acsDialogAreaEl.style = "";
|
||||
acpDialogTitleEl.textContent = "请再次输入密码";
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(50);
|
||||
acpAppBarEl.classList.add("color-reverse");
|
||||
acsDialogAreaEl.classList.remove("acp-ada-hidden");
|
||||
|
||||
const showFailedAnimation = async (el) => {
|
||||
el.classList.remove("invalid");
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(50);
|
||||
el.classList.add("invalid"); // Custom Anim
|
||||
el.classList.add("is-invalid"); // Bootstrap
|
||||
};
|
||||
|
||||
let resolveFn = null;
|
||||
const awaitCompletePromise = new Promise((resolve) => {
|
||||
resolveFn = resolve;
|
||||
});
|
||||
|
||||
const handleExit = async () => {
|
||||
const result = await awaitCompletePromise;
|
||||
if (result) {
|
||||
return { valid: true };
|
||||
} else {
|
||||
const inputEl = document.getElementById("auraSettingsPasswd");
|
||||
// @ts-expect-error
|
||||
inputEl.value = "";
|
||||
return { valid: false, hint: "未能验证密码, 请重试" };
|
||||
}
|
||||
};
|
||||
|
||||
const verifyPassword = async (_clickEvt) => {
|
||||
const inputEl = document.getElementById("acp-auth-user-input");
|
||||
const acpDialogTitleEl = document.getElementsByClassName(
|
||||
"acp-auth-dialog-title"
|
||||
)[0];
|
||||
// @ts-expect-error
|
||||
const userPasswdInput = inputEl.value;
|
||||
if (!userPasswdInput) {
|
||||
showFailedAnimation(inputEl);
|
||||
acpDialogTitleEl.textContent = "密码不能为空";
|
||||
}
|
||||
|
||||
const crypto = require("crypto");
|
||||
const encPasswd = crypto
|
||||
.createHash("sha512")
|
||||
.update(userPasswdInput + "EndlessX")
|
||||
.digest("hex")
|
||||
.toUpperCase();
|
||||
|
||||
if (encPasswd === password) {
|
||||
await global.__HUGO_AURA_UI_FUNCTIONS__.config.hideAndResetAuthDialog();
|
||||
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordWithSalt =
|
||||
password;
|
||||
if (global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig) {
|
||||
functions.handleEnableConfigEncryption("update", password);
|
||||
}
|
||||
if (resolveFn) resolveFn(true);
|
||||
return;
|
||||
} else {
|
||||
showFailedAnimation(inputEl);
|
||||
acpDialogTitleEl.textContent = "请再试一次";
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.resetAuthDialogInputElOnSubmit(
|
||||
verifyPassword
|
||||
);
|
||||
|
||||
// @ts-expect-error
|
||||
acpDialogConfirmBtnEl.onclick = verifyPassword;
|
||||
// @ts-expect-error
|
||||
acpDialogCancelBtnEl.onclick = (_evt) => {
|
||||
if (resolveFn) resolveFn(false);
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.handleNavBack();
|
||||
};
|
||||
|
||||
return await handleExit();
|
||||
},
|
||||
|
||||
getCurAccessMethodCount: () => {
|
||||
const fallbackMethods =
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
|
||||
.fallbackAccessMethods;
|
||||
const fallbackMethodsKeys = Object.keys(fallbackMethods);
|
||||
|
||||
let enabledCount = 0;
|
||||
|
||||
for (const method of fallbackMethodsKeys) {
|
||||
if (fallbackMethods[method]) {
|
||||
enabledCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.showEntryIcon) {
|
||||
enabledCount += 1;
|
||||
}
|
||||
|
||||
return enabledCount;
|
||||
},
|
||||
};
|
||||
|
||||
const auraSettings = [
|
||||
{
|
||||
id: 0,
|
||||
categoryName: "安全性",
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
id: "enableAuraSettingsPasswd",
|
||||
type: "switch",
|
||||
name: "启用访问密码",
|
||||
description: "启用后, Aura 设置 UI 需要输入密码才可访问",
|
||||
restart: false,
|
||||
reload: false,
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings
|
||||
.settingsPasswordEnabled;
|
||||
},
|
||||
callbackFn: async (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordEnabled =
|
||||
newVal;
|
||||
if (
|
||||
newVal &&
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig
|
||||
) {
|
||||
functions.handleEnableConfigEncryption("enc", null);
|
||||
} else if (
|
||||
!newVal &&
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig
|
||||
) {
|
||||
await global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
|
||||
global.__HUGO_AURA_CONFIG__,
|
||||
null
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
id: "enableConfigEncryption",
|
||||
type: "switch",
|
||||
name: "加密配置文件",
|
||||
description: "启用后, 本地配置文件将加密保存",
|
||||
restart: false,
|
||||
reload: false,
|
||||
tip: true,
|
||||
tipTitle: "配置文件将以 AES-256-GCM 加密算法在本地保存",
|
||||
warning: true,
|
||||
warningContent: "这可能导致性能问题",
|
||||
associateVal: ["auraSettings.settingsPasswordEnabled"],
|
||||
auraIf: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings
|
||||
.settingsPasswordEnabled;
|
||||
},
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig;
|
||||
},
|
||||
callbackFn: async (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig = newVal;
|
||||
if (newVal) {
|
||||
functions.handleEnableConfigEncryption("enc", null);
|
||||
} else {
|
||||
await global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
|
||||
global.__HUGO_AURA_CONFIG__,
|
||||
null
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
id: "auraSettingsPasswd",
|
||||
type: "input",
|
||||
subType: "password",
|
||||
name: "访问密码",
|
||||
description: "此密码将用于访问 Aura 设置 UI",
|
||||
restart: false,
|
||||
reload: false,
|
||||
tip: true,
|
||||
tipTitle: "密码将在本地使用 SHA512 加盐存储",
|
||||
associateVal: ["auraSettings.settingsPasswordEnabled"],
|
||||
auraIf: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings
|
||||
.settingsPasswordEnabled;
|
||||
},
|
||||
defaultValue: "",
|
||||
placeHolder: "留空表示不修改, 保留已设置值",
|
||||
valueGetter: () => {
|
||||
return "";
|
||||
},
|
||||
callbackFn: async (newVal) => {
|
||||
if (newVal === "" || !newVal) return { valid: true };
|
||||
if (newVal.length < 8)
|
||||
return { valid: false, hint: "请输入至少 8 位密码" };
|
||||
|
||||
const hasNumber = /[0-9]/.test(newVal);
|
||||
const hasLetter = /[a-zA-Z]/.test(newVal);
|
||||
const hasSpecial = /[^a-zA-Z0-9]/.test(newVal);
|
||||
|
||||
const typeCount = [hasNumber, hasLetter, hasSpecial].filter(
|
||||
Boolean
|
||||
).length;
|
||||
|
||||
if (typeCount < 2) {
|
||||
return {
|
||||
valid: false,
|
||||
hint: "请包含数字 / 字母 / 特殊字符中的至少 2 种",
|
||||
};
|
||||
}
|
||||
|
||||
const crypto = require("crypto");
|
||||
const result = crypto
|
||||
.createHash("sha512")
|
||||
.update(newVal + "EndlessX")
|
||||
.digest("hex")
|
||||
.toUpperCase();
|
||||
|
||||
return await functions.handle2ndPasswordPrompt(result);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
categoryName: "访问方式",
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
id: "showEntryIcon",
|
||||
type: "switch",
|
||||
name: "显示 HugoAura 设置图标",
|
||||
description: "控制 HugoAura 设置入口图标在管家首页的显示状态",
|
||||
restart: false,
|
||||
reload: true,
|
||||
tip: true,
|
||||
tipTitle: "禁用后, HugoAura 图标将不会出现在主页右上角",
|
||||
associateVal: [
|
||||
"auraSettings.uiAccessMethod.fallbackAccessMethods.hotkey",
|
||||
"auraSettings.uiAccessMethod.fallbackAccessMethods.touch",
|
||||
],
|
||||
auraIf: () => {
|
||||
return true;
|
||||
},
|
||||
auraDisable: () => {
|
||||
const fallbackMethods =
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
|
||||
.fallbackAccessMethods;
|
||||
const fallbackMethodsKeys = Object.keys(fallbackMethods);
|
||||
let anyEnabled = false;
|
||||
|
||||
for (const method of fallbackMethodsKeys) {
|
||||
if (fallbackMethods[method]) {
|
||||
anyEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
value: !anyEnabled,
|
||||
tooltip: !anyEnabled ? "至少启用一个备选访问方式" : "",
|
||||
};
|
||||
},
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
|
||||
.showEntryIcon;
|
||||
},
|
||||
callbackFn: async (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.showEntryIcon =
|
||||
newVal;
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
id: "allowHotkeyAccess",
|
||||
type: "switch",
|
||||
name: "使用快捷键打开 HugoAura 设置 UI",
|
||||
description:
|
||||
"启用后, 在管家首页按下 Ctrl + Shift + A 以打开 HugoAura 设置",
|
||||
restart: false,
|
||||
reload: true,
|
||||
associateVal: [
|
||||
"auraSettings.uiAccessMethod.showEntryIcon",
|
||||
"auraSettings.uiAccessMethod.fallbackAccessMethods.hotkey",
|
||||
"auraSettings.uiAccessMethod.fallbackAccessMethods.touch",
|
||||
],
|
||||
auraIf: () => {
|
||||
return true;
|
||||
},
|
||||
auraDisable: () => {
|
||||
const enableCount = functions.getCurAccessMethodCount();
|
||||
if (
|
||||
enableCount < 2 &&
|
||||
!global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
|
||||
.showEntryIcon &&
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
|
||||
.fallbackAccessMethods.hotkey
|
||||
) {
|
||||
return { value: true, tooltip: "无法禁用所有访问方式" };
|
||||
} else {
|
||||
return { value: false };
|
||||
}
|
||||
},
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
|
||||
.fallbackAccessMethods.hotkey;
|
||||
},
|
||||
callbackFn: async (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.fallbackAccessMethods.hotkey =
|
||||
newVal;
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
id: "allowTouchAccess",
|
||||
type: "switch",
|
||||
name: "使用触摸手势打开 HugoAura 设置 UI",
|
||||
description:
|
||||
"启用后, 在管家首页连击 7 次右上角信息栏 ( i ) 中的版本号区域以打开 HugoAura 设置",
|
||||
restart: false,
|
||||
reload: true,
|
||||
tip: true,
|
||||
tipTitle: "请在 10 秒钟内完成连击操作, 否则计时器将被重置",
|
||||
associateVal: [
|
||||
"auraSettings.uiAccessMethod.showEntryIcon",
|
||||
"auraSettings.uiAccessMethod.fallbackAccessMethods.hotkey",
|
||||
"auraSettings.uiAccessMethod.fallbackAccessMethods.touch",
|
||||
],
|
||||
auraIf: () => {
|
||||
return true;
|
||||
},
|
||||
auraDisable: () => {
|
||||
const enableCount = functions.getCurAccessMethodCount();
|
||||
if (
|
||||
enableCount < 2 &&
|
||||
!global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
|
||||
.showEntryIcon &&
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
|
||||
.fallbackAccessMethods.touch
|
||||
) {
|
||||
return { value: true, tooltip: "无法禁用所有访问方式" };
|
||||
} else {
|
||||
return { value: false };
|
||||
}
|
||||
},
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod
|
||||
.fallbackAccessMethods.touch;
|
||||
},
|
||||
callbackFn: async (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.uiAccessMethod.fallbackAccessMethods.touch =
|
||||
newVal;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
categoryName: "外观",
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
id: "actionBtnsOnRight",
|
||||
type: "switch",
|
||||
name: "顶栏操作类按钮右置",
|
||||
description: "启用后, 顶栏的<b>返回首页按钮</b>将靠右放置",
|
||||
restart: false,
|
||||
reload: false,
|
||||
auraIf: () => {
|
||||
return true;
|
||||
},
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings.appearance.appBar
|
||||
.actionBtnsOnRight;
|
||||
},
|
||||
callbackFn: async (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.appearance.appBar.actionBtnsOnRight =
|
||||
newVal;
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.initCustomUIProps(true);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = { auraSettings };
|
||||
@@ -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 };
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
})();
|
||||
|
||||
19
src/aura/ui/pages/windows/auraWsKeepAlive/index.html
Normal file
19
src/aura/ui/pages/windows/auraWsKeepAlive/index.html
Normal 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>
|
||||
@@ -1,33 +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":
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats.status = parsedWsMsg.data.status;
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats.version = parsedWsMsg.data.version;
|
||||
|
||||
global.ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.updatePlsStats`,
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats
|
||||
);
|
||||
|
||||
console.debug(
|
||||
"[HugoAura / UI / PLS Routes / DEBUG] Updated plsStats basic info:",
|
||||
global.__HUGO_AURA_GLOBAL__.plsStats
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = { basicRouteHandler };
|
||||
BIN
src/aura/ui/static/aikari_color_reversed.png
Normal file
BIN
src/aura/ui/static/aikari_color_reversed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB |
14
src/aura/utils/crypto.js
Executable file
14
src/aura/utils/crypto.js
Executable file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
const genRandomHex = () => {
|
||||
let result = "";
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const randomNum = Math.floor(Math.random() * 0x10000);
|
||||
result += randomNum.toString(16).padStart(4, "0");
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports = { genRandomHex };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user