[Feat] Basic PLS lifecycle mgmt

This commit is contained in:
Minoricew
2025-06-06 02:05:04 +08:00
parent 839afa79e8
commit 6da8348b41
18 changed files with 739 additions and 271 deletions

53
.gitignore vendored Normal file → Executable file
View File

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

View File

@@ -2,6 +2,7 @@
const __SCOPE = "main";
const os = require("os");
const fs = require("fs");
const path = require("path");
@@ -31,7 +32,7 @@ const applyPlsIpcHandler = (ipcMain) => {
* @returns {{ success: boolean; data: { isExists: boolean }; error?: Error }}
*/
(_event, _arg) => {
const plsFolderPath = path.join(__dirname, "../../../proxy");
const plsFolderPath = path.join("C:\\Program Files", "HugoAura PLS");
try {
const result = fs.existsSync(plsFolderPath);
return {
@@ -52,7 +53,7 @@ const applyPlsIpcHandler = (ipcMain) => {
`${methodBase}.getPlsStats`,
/**
*
* @returns {{ success: boolean; data: PLSStatus | null | undefined; }}
* @returns {{ success: boolean; data: import("../../../types/shared/pls/status").PLSStatus | null | undefined; }}
*/
(_event, _arg) => {
return {
@@ -67,7 +68,7 @@ const applyPlsIpcHandler = (ipcMain) => {
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {PLSStatus} arg
* @param {import("../../../types/shared/pls/status").PLSStatus} arg
* @returns
*/
(_event, arg) => {
@@ -103,6 +104,7 @@ const applyPlsIpcHandler = (ipcMain) => {
*/
(_event, arg) => {
global.__HUGO_AURA__.plsSettings = arg;
ipcMain.send("assistant", `${methodBase}.post.onPlsSettingsUpdate`, arg);
return {
success: true,
};
@@ -133,6 +135,7 @@ const applyPlsIpcHandler = (ipcMain) => {
*/
(_event, arg) => {
global.__HUGO_AURA__.plsRules = arg;
ipcMain.send("assistant", `${methodBase}.post.onPlsRulesUpdate`, arg);
return {
success: true,
};
@@ -185,6 +188,48 @@ const applyPlsIpcHandler = (ipcMain) => {
};
}
);
ipcMain.handle(
`${methodBase}.retryPlsConnect`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {any} arg
* @returns {{ success: boolean, status: "Already" | "Retrying" }}
*/
(_event, arg) => {
if (global.__HUGO_AURA__.plsStats?.connected) {
return {
success: true,
status: "Already",
};
} else {
ipcMain.send("desktopAssistant", `${methodBase}.retryPlsConnect`, arg);
return {
success: true,
status: "Retrying",
};
}
}
);
ipcMain.handle(
`${methodBase}.post.updateRetryStatus`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {{ success: boolean }} arg
*/
(_event, arg) => {
ipcMain.send("assistant", `${methodBase}.post.updateRetryStatus`, arg);
return {
success: true,
};
}
);
};
module.exports = { applyPlsIpcHandler };

View File

@@ -26,7 +26,6 @@
"enabled": true
}
},
"plsToken": "66ccff0d000721114514191981023333",
"auraSettings": {
"settingsPasswordEnabled": false,
"settingsPasswordWithSalt": "32703D292460CC9A3B867494D6AD9A8E4A3ADF0FAA4D6867BC4D412CC3927D02E47C6D0B1763BB53E57B2241C6193433561CDA09D7C48CA03983072B876F0965",

View File

@@ -3,6 +3,7 @@ import type EventBus from "../../utils/eventBus";
import { HookedWindowsMap, UIHooksMap, WindowHooksMap } from "../main/core";
import { UIHooksObject } from "../render/uiHook";
import ConfigManager from "../../init/shared/configManager";
import { PLSStatus } from "./pls/status";
type MainProcessOnlyVal<T> = T;
type RendererProcessOnlyVal<T> = T;
@@ -15,6 +16,7 @@ interface GlobalHugoAuraInfo {
plsRules?: Record<any, any> | null;
plsSettings?: Record<any, any> | null;
plsStats?: PLSStatus | null;
plsWs?: RendererProcessOnlyVal<WebSocket>;
uiHooks?: MainProcessOnlyVal<UIHooksMap>;
windowHooks?: MainProcessOnlyVal<WindowHooksMap>;
version: RendererProcessOnlyVal<string>;

View File

@@ -1,3 +1,5 @@
import { RendererProcessOnlyVal } from "../global";
interface PLSStatus {
installed: boolean;
detached: boolean;

View File

@@ -8,7 +8,7 @@ const updatePlsStatusFromLocal = async () => {
const plsStatus = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsStats`)
).data;
global.__HUGO_AURA_GLOBAL__.plsStatus = plsStatus;
global.__HUGO_AURA__.plsStats = plsStatus;
return plsStatus;
};
@@ -16,7 +16,7 @@ const updatePlsSettingsFromLocal = async () => {
const plsSettings = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsSettings`)
).data;
global.__HUGO_AURA_GLOBAL__.plsSettings = plsSettings;
global.__HUGO_AURA__.plsSettings = plsSettings;
return plsSettings;
};
@@ -24,7 +24,7 @@ const updatePlsRulesFromLocal = async () => {
const plsRules = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsRules`)
).data;
global.__HUGO_AURA_GLOBAL__.plsRules = plsRules;
global.__HUGO_AURA__.plsRules = plsRules;
return plsRules;
};
@@ -49,10 +49,11 @@ const genRandomHex = () => {
const updatePlsConfigToRemote = async (configKey, configValue) => {
const configLevels = configKey.split(".");
/** @type {Record<any, any>} */
// @ts-expect-error
let localUpdateTarget =
configLevels[0] === "ruleSettings"
? global.__HUGO_AURA_GLOBAL__.plsRules
: global.__HUGO_AURA_GLOBAL__.plsSettings;
? global.__HUGO_AURA__.plsRules
: global.__HUGO_AURA__.plsSettings;
for (const level of configLevels.slice(0, -1)) {
localUpdateTarget = localUpdateTarget[level];
}
@@ -80,8 +81,8 @@ const updatePlsConfigToRemote = async (configKey, configValue) => {
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,
basic: global.__HUGO_AURA__.plsSettings,
rules: global.__HUGO_AURA__.plsRules,
});
};

View File

@@ -22,7 +22,7 @@ const showRelaunchPLSToast = () => {
const toast = document.getElementById("relaunchPlsNotifyToast");
const toastBs = bootstrap.Toast.getOrCreateInstance(toast);
if (global.__HUGO_AURA_GLOBAL__.plsStatus.detached) {
if (global.__HUGO_AURA__.plsStats.detached) {
const relaunchBtn = document.getElementById("plsRelaunchBtn");
relaunchBtn.disabled = true;
relaunchBtn.textContent = "分离模式下无法执行";
@@ -71,6 +71,18 @@ const settingsRenderer = (pendingEl, settingsObj, isPls = false) => {
powerIcon.setAttribute("data-bs-title", "需要重启 Electron 进程");
entryTitle.appendChild(powerIcon);
}
if (entry.PLSRequired) {
const plsIcon = document.createElement("i");
plsIcon.classList.add(
"layui-icon",
"layui-icon-component",
"aura-settings-entry-property-icon"
);
plsIcon.setAttribute("data-bs-toggle", "tooltip");
plsIcon.setAttribute("data-bs-placement", "top");
plsIcon.setAttribute("data-bs-title", "需要 PLS 支持");
entryTitle.appendChild(plsIcon);
}
if (entry.restartPLS) {
const plsIcon = document.createElement("i");
plsIcon.classList.add(
@@ -217,6 +229,35 @@ const settingsRenderer = (pendingEl, settingsObj, isPls = false) => {
break;
}
const setDisableStatus = (el, isDisable, hint = null) => {
if (isDisable) {
el.classList.add("ase-operation-area-disabled");
if (hint) {
el.setAttribute("data-bs-toggle", "tooltip");
el.setAttribute("data-bs-placement", "top");
el.setAttribute("data-bs-title", hint);
}
} else {
el.setAttribute("data-bs-toggle", "");
el.setAttribute("data-bs-placement", "");
el.setAttribute("data-bs-title", "");
el.classList.remove("ase-operation-area-disabled");
}
};
if (entry.PLSRequired) {
if (!global.__HUGO_AURA__.plsStats.connected) {
setDisableStatus(entryOperationArea, true, "连接至 PLS 以继续");
}
document.addEventListener("onPLSStatsUpdate", (event) => {
if (event.detail.connected) {
setDisableStatus(entryOperationArea, false);
} else {
setDisableStatus(entryOperationArea, true, "连接至 PLS 以继续");
}
});
}
entryContainerEl.appendChild(entryOperationArea);
const isShow = entry.auraIf();
if (!isShow) entryContainerEl.classList.add("aura-settings-entry-hidden");
@@ -245,12 +286,7 @@ 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();
};
module.exports = { settingsRenderer };

View File

@@ -46,6 +46,15 @@
flex: 0.75;
}
.aura-settings-entry-operation-area.ase-operation-area-disabled {
opacity: 0.25;
cursor: not-allowed;
}
.aura-settings-entry-operation-area.ase-operation-area-disabled * {
pointer-events: none;
}
.aura-settings-entry-operation-area.form-switch {
transform: scale(1.2);
}

View File

@@ -65,7 +65,7 @@ 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/plsListener.js", "ui/bootstrap/bootstrap.bundle.min.js"],
onLoaded: `
console.log('[HugoAura / UI / Hooks / Assistant] Page loaded.');
`,

View File

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

View File

@@ -1,192 +1,233 @@
// @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 = {
(() => {
if (!global.__HUGO_AURA__.plsStats)
global.__HUGO_AURA__.plsStats = {
installed: false,
launched: false,
detached: false,
connected: false,
launched: false,
status: "unknown",
version: "未知",
status: "dead",
authToken: "66ccff0d000721114514191981023333",
authToken: "",
};
} else {
updatedPlsStats = curPlsStats.data;
}
const isPlsFolderExists = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsFolderExists`)
).data.isExists;
updatedPlsStats.installed = isPlsFolderExists;
const IPC_METHOD_BASE = "$aura.pls";
const REQUIRE_BASE = "../../aura/ui";
const __SCOPE = "desktopAssistant";
global.__HUGO_AURA_GLOBAL__.plsStats = updatedPlsStats;
console.debug(
"[HugoAura / UI / PLS Manager / DEBUG] Updated plsStats:",
global.__HUGO_AURA_GLOBAL__.plsStats
);
const { pushMsgHandler } = require(`${REQUIRE_BASE}/pls/pushHandler`);
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
updatedPlsStats
);
/** @type {number} */
let failedCounter = 0;
/** @type {boolean} */
let isErrorOccurred = false;
const startConnPls = () => {
createPlsConnection(updatedPlsStats.authToken, connectionResultCallback);
const sendRetryStatusToMain = (/** @type {Boolean} */ status) => {
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.post.updateRetryStatus`, {
success: status,
});
};
if (updatedPlsStats.detached && updatedPlsStats.installed) {
startConnPls();
}
/**
*
* @param {string} authToken
* @param {any} callback
* @returns
*/
const createPlsConnection = (authToken, callback) => {
if (failedCounter >= 3) {
console.error(
`[HugoAura / UI / PLS Manager / ERROR] Failed connecting to PLS WebSocket server, please check the status of PLS process.`
);
sendRetryStatusToMain(false);
return;
}
global.ipcRenderer.on(`${IPC_METHOD_BASE}.post.onPlsLaunched`, (_event) => {
setTimeout(() => {
/** @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 {import("electron").IpcRendererEvent} _evt
* @param {any} arg
*/
(_evt, arg) => {
wsObj.send(JSON.stringify(arg));
}
);
};
/**
*
* @param {boolean} result
* @param {WebSocket} wsObj
* @returns
*/
const connectionResultCallback = (result, wsObj) => {
if (!global.__HUGO_AURA__.plsStats) return; // 😅 typescript
global.__HUGO_AURA__.plsStats.launched = result;
global.__HUGO_AURA__.plsStats.connected = result;
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
if (!result) {
console.error(
`[HugoAura / UI / PLS Manager / ERROR] Failed connecting to PLS WebSocket server, retrying ...`
);
createPlsConnection(
global.__HUGO_AURA__.plsStats.authToken,
connectionResultCallback
);
return;
}
sendRetryStatusToMain(true);
global.__HUGO_AURA__.plsWs = wsObj;
registerSendReqListener(wsObj);
wsObj.onmessage = plsPushHandler;
};
/**
*
* @param {MessageEvent} event
*/
const plsPushHandler = (event) => {
try {
/** @type {Record<any, any>} */
const parsedEvent = JSON.parse(event.data);
console.debug(
"[HugoAura / UI / PLS Manager / DEBUG] Received new server message: "
);
if (!parsedEvent.eventId) {
// Push
pushMsgHandler(parsedEvent);
} else {
// Not push
global.ipcRenderer.send(
`${IPC_METHOD_BASE}.ws.broadcastMessageRecv`,
parsedEvent
);
}
} catch {
console.error(
"[HugoAura / UI / PLS Manager / ERROR] Failed to resolve server message: ",
event.data
);
}
};
const initPlsConnection = async () => {
if (!global.__HUGO_AURA__.plsStats) return;
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;
// @ts-expect-error
global.__HUGO_AURA__.plsStats = updatedPlsStats;
console.debug(
"[HugoAura / UI / PLS Manager / DEBUG] Updated early plsStats:",
global.__HUGO_AURA__.plsStats
);
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
updatedPlsStats
);
const startConnPls = () => {
createPlsConnection(updatedPlsStats.authToken, connectionResultCallback);
};
/*
if (updatedPlsStats.detached && updatedPlsStats.installed) {
*/
if (updatedPlsStats.installed) {
startConnPls();
}, 5000);
});
};
} else {
sendRetryStatusToMain(false);
}
const onSetup = () => {
if (!global.global.ipcRenderer) {
// @ts-ignore
global.global.ipcRenderer = require("electron").global.ipcRenderer;
}
/*
global.ipcRenderer.on(`${IPC_METHOD_BASE}.post.onPlsLaunched`, (_event) => {
setTimeout(() => {
startConnPls();
}, 5000);
});
*/
};
initPlsConnection();
};
const onSetup = () => {
if (!global.ipcRenderer) {
// @ts-ignore
global.ipcRenderer = require("electron").global.ipcRenderer;
}
initPlsConnection();
global.ipcRenderer.on(
`${IPC_METHOD_BASE}.retryPlsConnect`,
(_evt, _arg) => {
if (!global.__HUGO_AURA__.plsStats) return;
if (global.__HUGO_AURA__.plsStats.connected) return;
initPlsConnection();
}
);
};
(() => {
onSetup();
})();

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

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

View File

@@ -106,10 +106,9 @@
</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>

View File

@@ -18,12 +18,73 @@
}
.acs-bc-pls-status-page-main-logo {
max-width: 13.5%;
max-width: 11%;
opacity: 0.45;
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.acs-bc-psp-operations-container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}
.acs-bc-psp-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-pls-status-page-status-el {
display: flex;
align-items: center;

View File

@@ -9,6 +9,91 @@
class="acs-bc-pls-status-page-main-logo"
/>
<div class="acs-bc-psp-operations-container">
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Refresh">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M26 18A10 10 0 1 1 16 8h6.182l-3.584 3.585L20 13l6-6l-6-6l-1.402 1.414L22.185 6H16a12 12 0 1 0 12 12Z"
/>
</svg>
<p>刷新状态</p>
</div>
<div
class="acs-bc-psp-operation-btn"
aura-disabled="true"
id="acsBcPsp-operBtn-Download"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M23.5 22H23v-2h.5a4.5 4.5 0 0 0 .36-9H23l-.1-.82a7 7 0 0 0-13.88 0L9 11h-.86a4.5 4.5 0 0 0 .36 9H9v2h-.5A6.5 6.5 0 0 1 7.2 9.14a9 9 0 0 1 17.6 0A6.5 6.5 0 0 1 23.5 22"
/>
<path
fill="var(--svg-color)"
d="M17 26.17V14h-2v12.17l-2.59-2.58L11 25l5 5l5-5l-1.41-1.41z"
/>
</svg>
<p>下载内核</p>
</div>
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Install">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="m10 6l1.414-1.414L15 8.172V0h2v8.172l3.586-3.586L22 6l-6 6z"
/>
<path
fill="var(--svg-color)"
d="M22 16a5.98 5.98 0 0 0-1.757-4.243L16 16l-4.243-4.243A6 6 0 1 0 22 16"
/>
<path
fill="var(--svg-color)"
d="M30 16a13.96 13.96 0 0 0-4.105-9.895l-1.414 1.414a12 12 0 1 1-16.962 0L6.105 6.105A13.997 13.997 0 1 0 30 16"
/>
</svg>
<p>安装服务</p>
</div>
<div
class="acs-bc-psp-operation-btn acs-bc-psp-o-btn-dangerous"
id="acsBcPsp-operBtn-Uninstall"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M30 21.4L28.6 20L25 23.6L21.4 20L20 21.4l3.6 3.6l-3.6 3.6l1.4 1.4l3.6-3.6l3.6 3.6l1.4-1.4l-3.6-3.6z"
/>
<path
fill="var(--svg-color)"
d="M15.4 30L5 23.8c-.6-.4-1-1-1-1.7V9.9c0-.7.4-1.4 1-1.7l10-5.9c.3-.2.6-.3 1-.3s.7.1 1 .3l10 5.9c.6.4 1 1 1 1.7V16h-2V9.9L16 4L6 9.9v12.2l10.5 6.2z"
/>
</svg>
<p>卸载服务</p>
</div>
</div>
<div class="acs-bc-pls-status-page-status-el">
<p>安装状态</p>
<div

View File

@@ -1,11 +1,128 @@
if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig = {};
(() => {
const REQUIRE_BASE = "../../aura/ui/pages/configSubPages/behaviourCtrl";
const IPC_METHOD_BASE = "$aura.pls";
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus = {
updateOperationBtnStatus: async (btnName, side, btnContent = null) => {
const btnEl = document.getElementById(`acsBcPsp-operBtn-${btnName}`);
if (!btnEl) return false;
btnEl.setAttribute("aura-disabled", side ? "true" : "false");
if (btnContent) {
const btnPEl = btnEl.getElementsByTagName("p")[0];
btnPEl.textContent = btnContent;
}
if (side) {
btnEl.onclick = () => {};
} else {
switch (btnName) {
case "Refresh":
btnEl.onclick = () =>
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.refreshPlsStatus();
break;
case "Download":
break;
case "Install":
break;
case "Uninstall":
break;
default:
break;
}
}
return true;
},
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", "已安装");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Uninstall", false);
break;
case false:
updateStatusEl(acIdInst, atIdInst, "PENDING", "未安装");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Uninstall", true);
}
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;
}
},
refreshPlsStatus: async () => {
const updateOperationBtnStatus =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus
.updateOperationBtnStatus;
updateOperationBtnStatus("Refresh", true);
const result = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.retryPlsConnect`
);
if (result.success && result.status === "Retrying") {
updateOperationBtnStatus("Refresh", true, "正在重连");
ipcRenderer.once(
`${IPC_METHOD_BASE}.post.updateRetryStatus`,
async (_evt, _arg) => {
await global.__HUGO_AURA_GLOBAL__.utils.sleep(50);
updateOperationBtnStatus("Refresh", false, "刷新状态");
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatus();
}
);
} else if (result.success && result.status === "Already") {
updateOperationBtnStatus("Refresh", false, "刷新状态");
}
},
};
const GLOBAL_FUNCTIONS =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus;
const {
updatePlsStatusFromLocal,
} = require(`${REQUIRE_BASE}/../../../composables/plsConfigManager`);
const initBsTooltip = () => {
const tooltipTriggerList = document.querySelectorAll(
'[data-bs-toggle="tooltip"]'
);
const _tooltipList = [...tooltipTriggerList].map(
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
);
};
const updateStatusEl = (
areaContainerId,
areaTextId,
@@ -37,49 +154,13 @@
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();
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
initBsTooltip();
GLOBAL_FUNCTIONS.updateStatus();
document.addEventListener("onPLSStatsUpdate", () => {
GLOBAL_FUNCTIONS.updateStatus();
});
};
onMounted();

View File

@@ -9,6 +9,7 @@ const basicSettings = [
id: 0,
categoryName: "可访问性",
child: [
/*
{
index: 0,
id: "authToken",
@@ -18,7 +19,7 @@ const basicSettings = [
description: "选择一个安全的密钥, 用于 PLS 侧验证 Aura 前端身份",
restart: true,
reload: false,
restartPLS: false,
restartPLS: true,
associateVal: null,
auraIf: () => true,
defaultValue: "",
@@ -38,6 +39,39 @@ const basicSettings = [
return { valid: true };
},
},
*/
{
index: 0,
id: "plsListenPort",
type: "input",
subType: "text",
name: "PLS WS 监听端口",
description: "PLS 的 WebSocket 服务器将监听指定的端口",
restart: false,
reload: false,
PLSRequired: true,
restartPLS: true,
associateVal: null,
auraIf: () => true,
defaultValue: "",
placeHolder: "输入端口号 (10000 ~ 65535)",
valueGetter: () => {
if (!global.__HUGO_AURA__.plsSettings) return "";
return global.__HUGO_AURA__.plsSettings.wsPort;
},
callbackFn: (newVal) => {
if (newVal === "" || !newVal)
return { valid: false, hint: "请输入端口号" };
const numberNewVal = Number(newVal);
if (numberNewVal === NaN || !(10000 <= numberNewVal <= 65535)) {
return { valid: false, hint: "请输入合法的端口号 (10000 ~ 65535)" };
}
global.__HUGO_AURA__.plsSettings.wsPort = numberNewVal;
return { valid: true };
},
},
],
},
];

View File

@@ -11,17 +11,17 @@ 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.__HUGO_AURA__.plsStats.status = parsedWsMsg.data.status;
global.__HUGO_AURA__.plsStats.version = parsedWsMsg.data.version;
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA_GLOBAL__.plsStats
global.__HUGO_AURA__.plsStats
);
console.debug(
"[HugoAura / UI / PLS Routes / DEBUG] Updated plsStats basic info:",
global.__HUGO_AURA_GLOBAL__.plsStats
global.__HUGO_AURA__.plsStats
);
break;
default: