mirror of
https://github.com/HugoAura/Seewo-HugoAura.git
synced 2026-06-20 23:14:28 +08:00
[Feat] Further PLS ctrl & childProc perf improvements
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -22,7 +22,3 @@ desktop.ini
|
||||
|
||||
# Editor Files
|
||||
.vscode/
|
||||
|
||||
# Misc
|
||||
*.bak
|
||||
*.log
|
||||
|
||||
@@ -2,10 +2,64 @@
|
||||
|
||||
const __SCOPE = "main";
|
||||
|
||||
const os = require("os");
|
||||
const { exec } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
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) => {
|
||||
return new Promise((resolve) => {
|
||||
exec(`"${binPath}" ${command}`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`${logHeader} Failed to execute command:`, error);
|
||||
resolve({ success: false, errorObj: error });
|
||||
return;
|
||||
}
|
||||
resolve({ success: true });
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import("../../../types/main/electron").AuraIPCMain} ipcMain
|
||||
@@ -13,6 +67,10 @@ const path = require("path");
|
||||
const applyPlsIpcHandler = (ipcMain) => {
|
||||
const methodBase = "$aura.pls";
|
||||
|
||||
const PLS_INSTALL_DIR = path.join("C:\\Program Files", "HugoAura PLS");
|
||||
const PLS_BIN_PATH = path.join(PLS_INSTALL_DIR, "bin", "HugoAura-PLS.exe");
|
||||
const PLS_SVC_NAME = "HugoAuraPLS";
|
||||
|
||||
const isPlsDetached = process.argv.includes("--pls-detach");
|
||||
|
||||
global.__HUGO_AURA__.plsStats = {
|
||||
@@ -26,15 +84,14 @@ const applyPlsIpcHandler = (ipcMain) => {
|
||||
};
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.getPlsFolderExists`,
|
||||
`${methodBase}.getPlsBinExists`,
|
||||
/**
|
||||
*
|
||||
* @returns {{ success: boolean; data: { isExists: boolean }; error?: Error }}
|
||||
*/
|
||||
(_event, _arg) => {
|
||||
const plsFolderPath = path.join("C:\\Program Files", "HugoAura PLS");
|
||||
try {
|
||||
const result = fs.existsSync(plsFolderPath);
|
||||
const result = fs.existsSync(PLS_BIN_PATH);
|
||||
return {
|
||||
success: true,
|
||||
data: { isExists: result },
|
||||
@@ -189,6 +246,68 @@ const applyPlsIpcHandler = (ipcMain) => {
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.plsLifecycleQuery`,
|
||||
/**
|
||||
*
|
||||
* @param {import("electron").IpcMainInvokeEvent} _event
|
||||
* @param {{ target: import("../../../types/shared/pls/status").PLSLifecycleType }} arg
|
||||
* @returns {Promise<{ success: boolean, result: boolean }>}
|
||||
*/
|
||||
async (_event, arg) => {
|
||||
switch (arg.target) {
|
||||
case "isDetached":
|
||||
return { success: true, result: isPlsDetached };
|
||||
case "isSvcInstalled":
|
||||
return await functions.querySvcDetail(PLS_SVC_NAME, "SERVICE_NAME");
|
||||
case "isSvcStart":
|
||||
return await functions.querySvcDetail(PLS_SVC_NAME, "RUNNING");
|
||||
default:
|
||||
console.warn(
|
||||
`[HugoAura / IPC / PLS] <plsLifecycleQuery> Invalid arg.target: ${arg.target}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
result: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.plsLifecycleControl`,
|
||||
/**
|
||||
*
|
||||
* @param {*} _event
|
||||
* @param {{ target: import("../../../types/shared/pls/status").PLSLifecycleControlType }} arg
|
||||
* @returns {Promise<{ success: boolean, errorObj?: Error }>}
|
||||
*/
|
||||
async (_event, arg) => {
|
||||
const logHeader = "[HugoAura / IPC / PLS] <plsLifecycleControl>";
|
||||
|
||||
if (!global.__HUGO_AURA__.plsStats?.installed) {
|
||||
return { success: false, errorObj: new Error("PLS not installed") };
|
||||
}
|
||||
|
||||
switch (arg.target) {
|
||||
case "instSvc":
|
||||
return await functions.execCommand(
|
||||
logHeader,
|
||||
PLS_BIN_PATH,
|
||||
"install"
|
||||
);
|
||||
case "rmSvc":
|
||||
return await functions.execCommand(logHeader, PLS_BIN_PATH, "remove");
|
||||
case "startSvc":
|
||||
return await functions.execCommand(logHeader, PLS_BIN_PATH, "start");
|
||||
case "stopSvc":
|
||||
return await functions.execCommand(logHeader, PLS_BIN_PATH, "stop");
|
||||
default:
|
||||
return { success: false, errorObj: new Error("Method not found") };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
`${methodBase}.retryPlsConnect`,
|
||||
/**
|
||||
|
||||
@@ -164,12 +164,12 @@ class ConfigManager {
|
||||
/**
|
||||
*
|
||||
* @param {Record<any, any>} config
|
||||
* @returns {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
writeConfig(config) {
|
||||
async writeConfig(config) {
|
||||
try {
|
||||
if (this.useEncConfig) {
|
||||
const hashedPasswdResultObj = this.retrieveEncPassword();
|
||||
const hashedPasswdResultObj = await this.retrieveEncPasswordAsync();
|
||||
if (hashedPasswdResultObj.success && hashedPasswdResultObj.data) {
|
||||
this.encryptConfig(config, hashedPasswdResultObj.data);
|
||||
} else {
|
||||
@@ -261,7 +261,7 @@ class ConfigManager {
|
||||
*
|
||||
* @param {SHA256EncryptedPassword} password
|
||||
*/
|
||||
saveEncPassword(password) {
|
||||
async saveEncPassword(password) {
|
||||
let macAddr = this.priv_getMacAddr();
|
||||
let fallbackToStaticKey = false;
|
||||
|
||||
@@ -290,25 +290,25 @@ class ConfigManager {
|
||||
const ivHex = iv.toString("hex");
|
||||
const saltHex = randomSalt.toString("hex");
|
||||
|
||||
registryManager.createOrUpdateRegKey(
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_Value",
|
||||
encryptedPassword,
|
||||
true
|
||||
);
|
||||
registryManager.createOrUpdateRegKey(
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_IV",
|
||||
ivHex,
|
||||
true
|
||||
);
|
||||
registryManager.createOrUpdateRegKey(
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_Salt",
|
||||
saltHex,
|
||||
true
|
||||
);
|
||||
registryManager.createOrUpdateRegKey(
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_AuthTag",
|
||||
authTagHex,
|
||||
@@ -316,7 +316,7 @@ class ConfigManager {
|
||||
);
|
||||
|
||||
if (fallbackToStaticKey) {
|
||||
registryManager.createOrUpdateRegKey(
|
||||
await registryManager.createOrUpdateRegKey(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_FakeMac",
|
||||
macAddr,
|
||||
@@ -329,22 +329,22 @@ class ConfigManager {
|
||||
|
||||
retrieveEncPassword() {
|
||||
try {
|
||||
const authTagHex = registryManager.readRegKey(
|
||||
const authTagHex = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_AuthTag",
|
||||
true
|
||||
)?.data;
|
||||
const ivHex = registryManager.readRegKey(
|
||||
const ivHex = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_IV",
|
||||
true
|
||||
)?.data;
|
||||
const saltHex = registryManager.readRegKey(
|
||||
const saltHex = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_Salt",
|
||||
true
|
||||
)?.data;
|
||||
const encPasswdHex = registryManager.readRegKey(
|
||||
const encPasswdHex = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_Value",
|
||||
true
|
||||
@@ -353,7 +353,7 @@ class ConfigManager {
|
||||
let macAddr = null;
|
||||
|
||||
try {
|
||||
macAddr = registryManager.readRegKey(
|
||||
macAddr = registryManager.readRegKeySync(
|
||||
LMAK_SETTINGS_BASE,
|
||||
"LMAK_FakeMac",
|
||||
true
|
||||
@@ -433,8 +433,112 @@ class ConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
clearEncPasswdRegKey() {
|
||||
registryManager.delRegKey(LMAK_SETTINGS_BASE, null);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,7 +547,7 @@ class ConfigManager {
|
||||
* @param {SHA256EncryptedPassword} passwd
|
||||
*/
|
||||
encryptConfig(configData, passwd) {
|
||||
registryManager.initRegistry();
|
||||
registryManager.initRegistrySync();
|
||||
const salt = crypto.randomBytes(CRYPTO_SETTINGS_AES.saltLength);
|
||||
const key = crypto.pbkdf2Sync(
|
||||
passwd,
|
||||
@@ -617,9 +721,9 @@ class ConfigManager {
|
||||
*
|
||||
* @param {Record<any, any> | null} curConfig
|
||||
* @param {SHA256EncryptedPassword | undefined | null} passwd
|
||||
* @returns {{success: boolean}}
|
||||
* @returns {Promise<{success: boolean}>}
|
||||
*/
|
||||
switchToDecConfig(curConfig, passwd = null) {
|
||||
async switchToDecConfig(curConfig, passwd = null) {
|
||||
let decConfig = null;
|
||||
if (!curConfig && passwd) {
|
||||
const getDecConfigResult = this.decryptConfig(passwd);
|
||||
@@ -639,7 +743,7 @@ class ConfigManager {
|
||||
}
|
||||
|
||||
this.useEncConfig = false;
|
||||
this.clearEncPasswdRegKey();
|
||||
await this.clearEncPasswdRegKey();
|
||||
// @ts-expect-error
|
||||
this.writeConfig(curConfig ? curConfig : decConfig);
|
||||
try {
|
||||
|
||||
@@ -1,20 +1,52 @@
|
||||
// @ts-check
|
||||
|
||||
const childProc = require("child_process");
|
||||
const { exec, execSync } = require("child_process");
|
||||
|
||||
// Constants
|
||||
|
||||
const LOG_PREFIX = "[HugoAura / Init / Reg";
|
||||
const LOG_PREFIX_FUNC = "[HugoAura / Reg";
|
||||
const AURA_REGISTRY_PATH = ["HKCU", "SOFTWARE", "HugoAura"].join("\\");
|
||||
const AURA_REGISTRY_PATH = ["HKEY_USERS", ".DEFAULT", "SOFTWARE", "HugoAura"].join("\\");
|
||||
|
||||
class RegistryManager {
|
||||
/**
|
||||
* @param {string} [path]
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
handleCreateReg(path) {
|
||||
async handleCreateReg(path) {
|
||||
try {
|
||||
const createResult = childProc.execSync(["reg", "add", path].join(" "), {
|
||||
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",
|
||||
});
|
||||
|
||||
@@ -27,6 +59,8 @@ class RegistryManager {
|
||||
createResult
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
@@ -37,9 +71,37 @@ class RegistryManager {
|
||||
}
|
||||
}
|
||||
|
||||
initRegistry() {
|
||||
/**
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async initRegistry() {
|
||||
try {
|
||||
const queryResult = childProc.execSync(
|
||||
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" }
|
||||
);
|
||||
@@ -48,23 +110,81 @@ class RegistryManager {
|
||||
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.handleCreateReg(AURA_REGISTRY_PATH);
|
||||
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 }>}
|
||||
*/
|
||||
createOrUpdateRegKey(relativePath, keyName, keyVal, silent = false) {
|
||||
async createOrUpdateRegKey(relativePath, keyName, keyVal, silent = false) {
|
||||
try {
|
||||
const result = childProc.execSync(
|
||||
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",
|
||||
@@ -83,21 +203,76 @@ class RegistryManager {
|
||||
if (result) {
|
||||
if (!silent) {
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully created / updated reg key ${relativePath}/${keyName} with data: ${keyVal}`
|
||||
`${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] Add key command stdout:`,
|
||||
result
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Delete key command stdout:`,
|
||||
stdout
|
||||
);
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`${LOG_PREFIX_FUNC} / ERROR] Failed to create / update reg key, error:`,
|
||||
`${LOG_PREFIX_FUNC} / ERROR] Failed to delete reg key, error:`,
|
||||
silent ? "<Hidden>" : e
|
||||
);
|
||||
return {
|
||||
@@ -107,24 +282,21 @@ class RegistryManager {
|
||||
}
|
||||
}
|
||||
|
||||
/*>>> BUC <<<
|
||||
keyName === null --> delete the whole entry
|
||||
>>> EUC <<<*/
|
||||
/**
|
||||
*
|
||||
* @param {string} relativePath
|
||||
* @param {string | null} keyName
|
||||
* @param {boolean | undefined} silent
|
||||
* @returns {{ success: boolean, error: Error | null }}
|
||||
*/
|
||||
delRegKey(relativePath, keyName, silent = false) {
|
||||
delRegKeySync(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 string accepted.`
|
||||
`${LOG_PREFIX_FUNC} / CRITICAL] Arg \"keyName\" for function \"delRegKeySync\" cannot be undefined. Only null or string accepted.`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = childProc.execSync(
|
||||
const result = execSync(
|
||||
[
|
||||
"reg",
|
||||
"delete",
|
||||
@@ -150,6 +322,11 @@ class RegistryManager {
|
||||
success: true,
|
||||
error: null,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
@@ -164,14 +341,15 @@ class RegistryManager {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} relativePath
|
||||
* @param {string} keyName
|
||||
* @param {boolean | undefined} silent
|
||||
* @returns {Promise<{ success: boolean, data: string | null, error: Error | null }>}
|
||||
*/
|
||||
readRegKey(relativePath, keyName, silent = false) {
|
||||
async readRegKey(relativePath, keyName, silent = false) {
|
||||
try {
|
||||
const readResult = childProc.execSync(
|
||||
const { stdout } = await new Promise((resolve, reject) => {
|
||||
exec(
|
||||
[
|
||||
"reg",
|
||||
"query",
|
||||
@@ -179,20 +357,25 @@ class RegistryManager {
|
||||
"/v",
|
||||
`\"${keyName}\"`,
|
||||
].join(" "),
|
||||
{ encoding: "utf8" }
|
||||
{ encoding: "utf8" },
|
||||
(error, stdout, stderr) => {
|
||||
if (error) reject(error);
|
||||
else resolve({ stdout, stderr });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (readResult) {
|
||||
if (!silent) {
|
||||
console.debug(
|
||||
`${LOG_PREFIX}] Successfully read reg key ${relativePath}/${keyName}, stdout:`,
|
||||
readResult
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${relativePath}/${keyName}, stdout:`,
|
||||
stdout
|
||||
);
|
||||
}
|
||||
const match = readResult.match(/REG_SZ\s+(.+)/);
|
||||
|
||||
const match = stdout.match(/REG_SZ\s+(.+)/);
|
||||
|
||||
if (!match) {
|
||||
console.warn(`${LOG_PREFIX_FUNC} / WARN] Data not found in stdout`);
|
||||
console.warn(`${LOG_PREFIX} / WARN] Data not found in stdout`);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
@@ -206,10 +389,72 @@ class RegistryManager {
|
||||
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} relativePath
|
||||
* @param {string} keyName
|
||||
* @param {boolean | undefined} silent
|
||||
* @returns {{ success: boolean, data: string | null, error: Error | null }}
|
||||
*/
|
||||
readRegKeySync(relativePath, keyName, silent = false) {
|
||||
try {
|
||||
const readResult = execSync(
|
||||
[
|
||||
"reg",
|
||||
"query",
|
||||
[AURA_REGISTRY_PATH, relativePath].join("\\"),
|
||||
"/v",
|
||||
`\"${keyName}\"`,
|
||||
].join(" "),
|
||||
{ encoding: "utf8" }
|
||||
);
|
||||
|
||||
if (readResult) {
|
||||
if (!silent) {
|
||||
console.debug(
|
||||
`${LOG_PREFIX_FUNC} / SUCCESS] Successfully read reg key ${relativePath}/${keyName}, stdout:`,
|
||||
readResult
|
||||
);
|
||||
}
|
||||
const match = 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_FUNC} / ERROR] Failed to read reg key, error:`,
|
||||
`${LOG_PREFIX} / ERROR] Failed to read reg key, error:`,
|
||||
silent ? "<Hidden>" : e
|
||||
);
|
||||
return {
|
||||
|
||||
1
src/aura/types/shared/global.d.ts
vendored
1
src/aura/types/shared/global.d.ts
vendored
@@ -26,6 +26,7 @@ type GlobalHugoAuraConfig = AuraConfig;
|
||||
|
||||
declare global {
|
||||
var ipcRenderer: RendererProcessOnlyVal<IpcRenderer>;
|
||||
var _ACCEPT_DATA: any;
|
||||
var __HUGO_AURA__: GlobalHugoAuraInfo;
|
||||
var __HUGO_AURA_CONFIG__: GlobalHugoAuraConfig;
|
||||
var __HUGO_AURA_CONFIG_MGR__: ConfigManager;
|
||||
|
||||
4
src/aura/types/shared/pls/status.d.ts
vendored
4
src/aura/types/shared/pls/status.d.ts
vendored
@@ -9,3 +9,7 @@ interface PLSStatus {
|
||||
version: string;
|
||||
authToken: string;
|
||||
}
|
||||
|
||||
type PLSLifecycleType = "isDetached" | "isSvcInstalled" | "isSvcStart";
|
||||
|
||||
type PLSLifecycleControlType = "instSvc" | "rmSvc" | "startSvc" | "stopSvc";
|
||||
|
||||
@@ -61,7 +61,7 @@ const updatePlsConfigToRemote = async (configKey, configValue) => {
|
||||
|
||||
const plsConfigUpdateEvent = new CustomEvent("onPLSConfigUpdate", {
|
||||
detail: {
|
||||
path: configKey,
|
||||
path: configLevels,
|
||||
value: configValue,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -41,15 +41,97 @@ const showToast = (entry) => {
|
||||
}
|
||||
};
|
||||
|
||||
const settingsRenderer = (pendingEl, settingsObj, isPls = false) => {
|
||||
const formEl = document.createElement("form");
|
||||
formEl.classList.add("aura-settings-form");
|
||||
for (const category of settingsObj) {
|
||||
const categoryTitleEl = document.createElement("p");
|
||||
categoryTitleEl.classList.add("aura-settings-category-header");
|
||||
categoryTitleEl.textContent = category.categoryName;
|
||||
formEl.appendChild(categoryTitleEl);
|
||||
for (const entry of category.child) {
|
||||
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 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);
|
||||
});
|
||||
operationArea.classList.add("form-check", "form-switch");
|
||||
return switchEl;
|
||||
}
|
||||
case "radio": {
|
||||
const elValue = entry.valueGetter();
|
||||
const elArr = [];
|
||||
for (const template of entry.templates) {
|
||||
const inlineContainerEl = document.createElement("div");
|
||||
inlineContainerEl.classList.add("form-check", "form-check-inline");
|
||||
const radioEl = document.createElement("input");
|
||||
radioEl.value = template;
|
||||
radioEl.classList.add("form-check-input");
|
||||
radioEl.type = "radio";
|
||||
radioEl.name = `${entry.id}Radios`;
|
||||
radioEl.id = `${entry.id}Radio${entry.templates.indexOf(template)}`;
|
||||
radioEl.checked = template === elValue ? true : false;
|
||||
radioEl.addEventListener("change", async (event) => {
|
||||
if (event.target.checked) {
|
||||
showToast(entry);
|
||||
await entry.callbackFn(event.target.value);
|
||||
}
|
||||
});
|
||||
inlineContainerEl.appendChild(radioEl);
|
||||
const labelEl = document.createElement("label");
|
||||
labelEl.classList.add("form-check-label");
|
||||
labelEl.setAttribute("for", radioEl.id);
|
||||
labelEl.textContent =
|
||||
entry.templateLabels[entry.templates.indexOf(template)];
|
||||
inlineContainerEl.appendChild(labelEl);
|
||||
elArr.push(inlineContainerEl);
|
||||
}
|
||||
return elArr;
|
||||
}
|
||||
case "input": {
|
||||
const inputEl = document.createElement("input");
|
||||
inputEl.classList.add("form-control");
|
||||
inputEl.type = entry.subType;
|
||||
inputEl.value = entry.valueGetter();
|
||||
inputEl.placeholder = entry.placeHolder;
|
||||
inputEl.id = entry.id;
|
||||
inputEl.addEventListener("change", async (event) => {
|
||||
const result = await entry.callbackFn(event.target.value);
|
||||
const success = result.valid;
|
||||
if (success) {
|
||||
showToast(entry);
|
||||
|
||||
if (inputEl.className.includes("is-invalid")) {
|
||||
inputEl.classList.remove("is-invalid");
|
||||
descriptionArea.textContent = entry.description;
|
||||
descriptionArea.classList.remove("ase-desc-error-hint");
|
||||
}
|
||||
} else {
|
||||
inputEl.classList.add("is-invalid");
|
||||
descriptionArea.textContent = result.hint;
|
||||
descriptionArea.classList.add("ase-desc-error-hint");
|
||||
}
|
||||
});
|
||||
operationArea.classList.add("ase-operation-area-expanded");
|
||||
return inputEl;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const renderChild = (entry, formEl) => {
|
||||
const entryContainerEl = document.createElement("div");
|
||||
entryContainerEl.classList.add("aura-settings-entry");
|
||||
entryContainerEl.id = `${entry.id}Container`;
|
||||
@@ -142,91 +224,25 @@ const settingsRenderer = (pendingEl, settingsObj, isPls = false) => {
|
||||
|
||||
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", async (event) => {
|
||||
showToast(entry);
|
||||
await 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", async (event) => {
|
||||
if (event.target.checked) {
|
||||
showToast(entry);
|
||||
await 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", async (event) => {
|
||||
const result = await 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");
|
||||
let targetEl = renderInputArea(entry, entryOperationArea, entryDescription);
|
||||
insertOrRemoveEl(entryOperationArea, targetEl, true);
|
||||
|
||||
if (entry.reactive) {
|
||||
document.addEventListener(
|
||||
entry.PLSRequired ? "onPLSConfigUpdate" : "onHugoAuraConfigUpdate",
|
||||
(event) => {
|
||||
if (entry.reactiveVal.includes(event.detail.path.join("."))) {
|
||||
insertOrRemoveEl(entryOperationArea, targetEl, false);
|
||||
targetEl = renderInputArea(
|
||||
entry,
|
||||
entryOperationArea,
|
||||
entryDescription
|
||||
);
|
||||
insertOrRemoveEl(entryOperationArea, targetEl, true);
|
||||
}
|
||||
} 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;
|
||||
);
|
||||
}
|
||||
|
||||
const setDisableStatus = (el, isDisable, hint = null) => {
|
||||
@@ -264,10 +280,9 @@ const settingsRenderer = (pendingEl, settingsObj, isPls = false) => {
|
||||
|
||||
if (entry.associateVal) {
|
||||
document.addEventListener(
|
||||
isPls ? "onPLSConfigUpdate" : "onHugoAuraConfigUpdate",
|
||||
entry.PLSRequired ? "onPLSConfigUpdate" : "onHugoAuraConfigUpdate",
|
||||
(event) => {
|
||||
if (!entry.associateVal.includes(event.detail.path.join(".")))
|
||||
return;
|
||||
if (!entry.associateVal.includes(event.detail.path.join("."))) return;
|
||||
const cls = entryContainerEl.classList;
|
||||
const isShow = entry.auraIf();
|
||||
isShow
|
||||
@@ -278,6 +293,18 @@ const settingsRenderer = (pendingEl, settingsObj, isPls = false) => {
|
||||
}
|
||||
|
||||
formEl.appendChild(entryContainerEl);
|
||||
};
|
||||
|
||||
const settingsRenderer = (pendingEl, settingsObj) => {
|
||||
const formEl = document.createElement("form");
|
||||
formEl.classList.add("aura-settings-form");
|
||||
for (const category of settingsObj) {
|
||||
const categoryTitleEl = document.createElement("p");
|
||||
categoryTitleEl.classList.add("aura-settings-category-header");
|
||||
categoryTitleEl.textContent = category.categoryName;
|
||||
formEl.appendChild(categoryTitleEl);
|
||||
for (const entry of category.child) {
|
||||
renderChild(entry, formEl);
|
||||
}
|
||||
|
||||
const hrEl = document.createElement("hr");
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
.aura-settings-entry-warning-icon {
|
||||
transform: rotate(180deg);
|
||||
display: inline-block;
|
||||
color: rgb(241, 155, 0);
|
||||
color: rgb(147, 0, 0);
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
||||
@@ -25,3 +25,7 @@ button.nav-link {
|
||||
.nav-underline {
|
||||
--bs-nav-underline-link-active-color: #0d6efd !important;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
--bs-tooltip-max-width: 400px !important;
|
||||
}
|
||||
|
||||
@@ -15,19 +15,91 @@
|
||||
const REQUIRE_BASE = "../../aura/ui";
|
||||
const __SCOPE = "desktopAssistant";
|
||||
|
||||
const PLS_REG_PATH = "ProxyLayerServices";
|
||||
|
||||
const { pushMsgHandler } = require(`${REQUIRE_BASE}/pls/pushHandler`);
|
||||
const RegistryManager = require(`${REQUIRE_BASE}/../init/shared/registryManager`);
|
||||
|
||||
const registryManager = new RegistryManager();
|
||||
|
||||
/** @type {number} */
|
||||
let failedCounter = 0;
|
||||
/** @type {boolean} */
|
||||
let isErrorOccurred = false;
|
||||
|
||||
/** @type {number} */
|
||||
let plsPort = 22077;
|
||||
/** @type {"wss" | "ws"} */
|
||||
let plsProtocol = "wss";
|
||||
|
||||
/** @type {boolean} */
|
||||
let isRetrying = false;
|
||||
|
||||
/** @type {any} */
|
||||
let curSendListener = null;
|
||||
|
||||
const sendRetryStatusToMain = (/** @type {Boolean} */ status) => {
|
||||
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.post.updateRetryStatus`, {
|
||||
success: status,
|
||||
});
|
||||
};
|
||||
|
||||
const calcFullAuthToken = (/** @type {string} */ authToken) => {
|
||||
const trustToken = window._ACCEPT_DATA.getData("deviceId");
|
||||
const conjToken = authToken + "AuraXAuth" + trustToken + "NeverEnds";
|
||||
const crypto = require("crypto");
|
||||
return crypto.createHash("sha512").update(conjToken).digest("hex");
|
||||
};
|
||||
|
||||
const clearIpcListener = () => {
|
||||
if (curSendListener) {
|
||||
global.ipcRenderer.off(
|
||||
`${IPC_METHOD_BASE}.ws.post.onReqSendMsg`,
|
||||
curSendListener
|
||||
);
|
||||
curSendListener = null;
|
||||
}
|
||||
};
|
||||
|
||||
const startConnPlsProc = async (updatedPlsStats) => {
|
||||
const authTokenRet = await registryManager.readRegKey(
|
||||
PLS_REG_PATH,
|
||||
"AuthToken",
|
||||
true
|
||||
);
|
||||
if (authTokenRet.success) {
|
||||
updatedPlsStats.authToken = authTokenRet.data;
|
||||
// @ts-expect-error
|
||||
global.__HUGO_AURA__.plsStats.authToken = authTokenRet.data;
|
||||
} else {
|
||||
sendRetryStatusToMain(false);
|
||||
return;
|
||||
}
|
||||
const portRet = await registryManager.readRegKey(
|
||||
PLS_REG_PATH,
|
||||
"WsPort",
|
||||
true
|
||||
);
|
||||
if (portRet.success) {
|
||||
try {
|
||||
plsPort = Number(portRet.data);
|
||||
} catch {
|
||||
console.warn(
|
||||
`[HugoAura / UI / PLS Manager] Invalid PLS port: ${portRet.data}`
|
||||
);
|
||||
}
|
||||
}
|
||||
const protoRet = await registryManager.readRegKey(
|
||||
PLS_REG_PATH,
|
||||
"Protocol",
|
||||
true
|
||||
);
|
||||
if (protoRet.success) {
|
||||
plsProtocol = protoRet.data;
|
||||
}
|
||||
createPlsConnection(updatedPlsStats.authToken, connectionResultCallback);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} authToken
|
||||
@@ -43,9 +115,11 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const fullAuthToken = calcFullAuthToken(authToken);
|
||||
|
||||
/** @type {WebSocket} */
|
||||
const plsWs = new WebSocket(
|
||||
`wss://pls.hugoaura.local:22077/?auth=${authToken}`
|
||||
`${plsProtocol}://pls.hugoaura.local:${plsPort}/?auth=${fullAuthToken}`
|
||||
);
|
||||
|
||||
plsWs.onopen = () => {
|
||||
@@ -59,6 +133,26 @@
|
||||
};
|
||||
|
||||
plsWs.onclose = () => {
|
||||
clearIpcListener();
|
||||
if (global.__HUGO_AURA__.plsStats) {
|
||||
if (global.__HUGO_AURA__.plsStats.status === "notReady") {
|
||||
if (isRetrying) {
|
||||
sendRetryStatusToMain(false);
|
||||
return;
|
||||
}
|
||||
console.warn(
|
||||
"[HugoAura / UI / PLS Manager / WARN] PLS not ready, try again after 10s..."
|
||||
);
|
||||
isRetrying = true;
|
||||
setTimeout(async () => {
|
||||
isRetrying = false;
|
||||
startConnPlsProc(global.__HUGO_AURA__.plsStats);
|
||||
}, 10000);
|
||||
sendRetryStatusToMain(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.error(
|
||||
"[HugoAura / UI / PLS Manager / ERROR] WebSocket connection closed."
|
||||
);
|
||||
@@ -73,16 +167,18 @@
|
||||
* @param {WebSocket} wsObj
|
||||
*/
|
||||
const registerSendReqListener = (wsObj) => {
|
||||
global.ipcRenderer.on(
|
||||
`${IPC_METHOD_BASE}.ws.post.onReqSendMsg`,
|
||||
clearIpcListener();
|
||||
/**
|
||||
*
|
||||
* @param {import("electron").IpcRendererEvent} _evt
|
||||
* @param {any} arg
|
||||
*/
|
||||
(_evt, arg) => {
|
||||
curSendListener = (_evt, arg) => {
|
||||
wsObj.send(JSON.stringify(arg));
|
||||
}
|
||||
};
|
||||
global.ipcRenderer.on(
|
||||
`${IPC_METHOD_BASE}.ws.post.onReqSendMsg`,
|
||||
curSendListener
|
||||
);
|
||||
};
|
||||
|
||||
@@ -151,6 +247,11 @@
|
||||
const initPlsConnection = async () => {
|
||||
if (!global.__HUGO_AURA__.plsStats) return;
|
||||
|
||||
if (isRetrying) {
|
||||
sendRetryStatusToMain(false);
|
||||
return;
|
||||
}
|
||||
|
||||
failedCounter = 0;
|
||||
isErrorOccurred = false;
|
||||
|
||||
@@ -173,7 +274,7 @@
|
||||
}
|
||||
|
||||
const isPlsFolderExists = (
|
||||
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsFolderExists`)
|
||||
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsBinExists`)
|
||||
).data.isExists;
|
||||
updatedPlsStats.installed = isPlsFolderExists;
|
||||
|
||||
@@ -189,15 +290,11 @@
|
||||
updatedPlsStats
|
||||
);
|
||||
|
||||
const startConnPls = () => {
|
||||
createPlsConnection(updatedPlsStats.authToken, connectionResultCallback);
|
||||
};
|
||||
|
||||
/*
|
||||
if (updatedPlsStats.detached && updatedPlsStats.installed) {
|
||||
*/
|
||||
if (updatedPlsStats.installed) {
|
||||
startConnPls();
|
||||
await startConnPlsProc(updatedPlsStats);
|
||||
} else {
|
||||
sendRetryStatusToMain(false);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,13 @@
|
||||
`${IPC_METHOD_BASE}.post.onPlsStatsUpdate`,
|
||||
(_event, arg) => {
|
||||
global.__HUGO_AURA__.plsStats = arg;
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("onPLSStatsUpdate", {
|
||||
detail: {
|
||||
connected: arg.connected,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -21,6 +28,14 @@
|
||||
`${IPC_METHOD_BASE}.post.onPlsSettingsUpdate`,
|
||||
(_event, arg) => {
|
||||
global.__HUGO_AURA__.plsSettings = arg;
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("onPLSConfigUpdate", {
|
||||
detail: {
|
||||
path: ["root", "settings"],
|
||||
value: arg,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -28,6 +43,14 @@
|
||||
`${IPC_METHOD_BASE}.post.onPlsRulesUpdate`,
|
||||
(_event, arg) => {
|
||||
global.__HUGO_AURA__.plsRules = arg;
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("onPLSConfigUpdate", {
|
||||
detail: {
|
||||
path: ["root", "ruleSettings"],
|
||||
value: arg,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -189,6 +189,11 @@
|
||||
<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>
|
||||
@@ -217,6 +222,11 @@
|
||||
<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>
|
||||
@@ -245,6 +255,11 @@
|
||||
<div class="toast-header">
|
||||
<i class="layui-icon layui-icon-tips"></i>
|
||||
<strong class="me-auto">重启 PLS 进程以应用设置</strong>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="toast"
|
||||
></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
<p>请重启 PLS 进程以应用修改的设置</p>
|
||||
|
||||
@@ -137,3 +137,62 @@
|
||||
.acs-bc-pls-status-page-status-area.failed p {
|
||||
color: rgb(175, 0, 0);
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.warning
|
||||
.acs-bc-pls-status-page-status-area-circle {
|
||||
background-color: rgb(212, 127, 0);
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.warning p {
|
||||
color: rgb(212, 127, 0);
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.info
|
||||
.acs-bc-pls-status-page-status-area-circle {
|
||||
background-color: #3d78ff;
|
||||
}
|
||||
|
||||
.acs-bc-pls-status-page-status-area.info p {
|
||||
color: #3d78ff;
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast.body-display-none .toast-body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast.body-display-none .toast-header {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[closable="false"] .btn-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="success"] .toast-header {
|
||||
background-color: rgb(6, 196, 65);
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="warning"] .toast-header {
|
||||
background-color: rgb(225, 178, 44);
|
||||
color: rgb(255 255 255);
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="error"] .toast-header {
|
||||
background-color: rgb(217, 57, 4);
|
||||
color: rgb(255 255 255);
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="info"] .toast-header {
|
||||
background-color: rgb(0, 149, 222);
|
||||
color: rgb(255 255 255);
|
||||
}
|
||||
|
||||
.acs-bc-psp-toast[variant="success"] .btn-close,
|
||||
.acs-bc-psp-toast[variant="warning"] .btn-close,
|
||||
.acs-bc-psp-toast[variant="error"] .btn-close,
|
||||
.acs-bc-psp-toast[variant="info"] .btn-close {
|
||||
color: rgb(255, 255, 255);
|
||||
filter: invert(1);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,36 @@
|
||||
</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-pls-status-page-status-el">
|
||||
@@ -133,4 +163,18 @@
|
||||
<p id="acs-bc-psp-version-text">不可用</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||
<div id="plsStatusNotifyToast" class="acs-bc-psp-toast toast" data-bs-autohide="false">
|
||||
<div class="toast-header">
|
||||
<strong class="me-auto" id="plsStatusNotifyToastTitle"></strong>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="toast"
|
||||
></button>
|
||||
</div>
|
||||
<div class="toast-body" id="plsStatusNotifyToastBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,32 +1,254 @@
|
||||
if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig = {};
|
||||
|
||||
if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
|
||||
global.__HUGO_AURA_UI_REACTIVES__.subConfig = {};
|
||||
|
||||
(() => {
|
||||
const REQUIRE_BASE = "../../aura/ui/pages/configSubPages/behaviourCtrl";
|
||||
const IPC_METHOD_BASE = "$aura.pls";
|
||||
|
||||
const lifecycleStatus = {
|
||||
installed: false,
|
||||
detached: false,
|
||||
svcInstalled: false,
|
||||
svcRunning: false,
|
||||
};
|
||||
|
||||
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus = {
|
||||
toastAutoHideTimeout: null,
|
||||
};
|
||||
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus = {
|
||||
updateOperationBtnStatus: async (btnName, side, btnContent = null) => {
|
||||
updateToast: async (
|
||||
variant,
|
||||
title,
|
||||
body = null,
|
||||
closable = true,
|
||||
autoHide = true,
|
||||
hideAfter = 3000
|
||||
) => {
|
||||
const toastRootEl = document.getElementById("plsStatusNotifyToast");
|
||||
const toastHeaderEl = document.getElementById(
|
||||
"plsStatusNotifyToastTitle"
|
||||
);
|
||||
const toastBodyEl = document.getElementById("plsStatusNotifyToastBody");
|
||||
|
||||
const bsToastIns = bootstrap.Toast.getOrCreateInstance(toastRootEl);
|
||||
if (bsToastIns.isShown) {
|
||||
bsToastIns.hide();
|
||||
const timeout =
|
||||
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus
|
||||
.toastAutoHideTimeout;
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(160);
|
||||
}
|
||||
|
||||
toastRootEl.setAttribute("variant", variant);
|
||||
toastHeaderEl.innerHTML = title;
|
||||
if (body) {
|
||||
toastBodyEl.innerHTML = body;
|
||||
toastRootEl.classList.remove("body-display-none");
|
||||
} else {
|
||||
toastRootEl.classList.add("body-display-none");
|
||||
}
|
||||
|
||||
toastRootEl.setAttribute("closable", closable.toString());
|
||||
|
||||
bsToastIns.show();
|
||||
if (autoHide && hideAfter) {
|
||||
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus.toastAutoHideTimeout =
|
||||
setTimeout(() => {
|
||||
bsToastIns.hide();
|
||||
}, hideAfter);
|
||||
}
|
||||
},
|
||||
|
||||
updateOperationBtnStatus: async (
|
||||
btnName,
|
||||
isDisabled,
|
||||
btnContent = null
|
||||
) => {
|
||||
const btnEl = document.getElementById(`acsBcPsp-operBtn-${btnName}`);
|
||||
if (!btnEl) return false;
|
||||
btnEl.setAttribute("aura-disabled", side ? "true" : "false");
|
||||
btnEl.setAttribute("aura-disabled", isDisabled ? "true" : "false");
|
||||
if (btnContent) {
|
||||
const btnPEl = btnEl.getElementsByTagName("p")[0];
|
||||
btnPEl.textContent = btnContent;
|
||||
}
|
||||
if (side) {
|
||||
if (isDisabled) {
|
||||
btnEl.onclick = () => {};
|
||||
} else {
|
||||
switch (btnName) {
|
||||
case "Refresh":
|
||||
btnEl.onclick = () =>
|
||||
btnEl.onclick = () => {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"warning",
|
||||
"正在更新",
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
);
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.refreshPlsStatus();
|
||||
};
|
||||
break;
|
||||
case "Download":
|
||||
break;
|
||||
|
||||
// ↓ 这边的确可以把这些全都合并到一个可复用 fn 里去, 但没必要
|
||||
case "Install":
|
||||
btnEl.onclick = async () => {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"info",
|
||||
"正在请求安装",
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
);
|
||||
const ret = await ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.plsLifecycleControl`,
|
||||
{ target: "instSvc" }
|
||||
);
|
||||
if (ret.success) {
|
||||
lifecycleStatus.svcInstalled = true;
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"success",
|
||||
"服务安装成功",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
2000
|
||||
);
|
||||
} else {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"error",
|
||||
"服务安装失败",
|
||||
"<p>检查日志以获取详细信息</p>",
|
||||
true,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
|
||||
};
|
||||
break;
|
||||
case "Uninstall":
|
||||
btnEl.onclick = async () => {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"info",
|
||||
"正在请求卸载",
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
);
|
||||
const ret = await ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.plsLifecycleControl`,
|
||||
{ target: "rmSvc" }
|
||||
);
|
||||
if (ret.success) {
|
||||
lifecycleStatus.svcInstalled = false;
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"success",
|
||||
"服务卸载成功",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
2000
|
||||
);
|
||||
} else {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"error",
|
||||
"服务卸载失败",
|
||||
"<p>检查日志以获取详细信息</p>",
|
||||
true,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
|
||||
};
|
||||
break;
|
||||
case "Start":
|
||||
btnEl.onclick = async () => {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"info",
|
||||
"正在请求启动",
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
);
|
||||
const ret = await ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.plsLifecycleControl`,
|
||||
{ target: "startSvc" }
|
||||
);
|
||||
if (ret.success) {
|
||||
lifecycleStatus.svcRunning = true;
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"success",
|
||||
"PLS 已启动",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
2000
|
||||
);
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(100);
|
||||
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.retryPlsConnect`);
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
|
||||
} else {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"error",
|
||||
"PLS 启动失败",
|
||||
"<p>检查 PLS 日志目录以获取详细信息</p>",
|
||||
true,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
};
|
||||
break;
|
||||
case "Stop":
|
||||
btnEl.onclick = async () => {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"info",
|
||||
"正在请求停止",
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
);
|
||||
const ret = await ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.plsLifecycleControl`,
|
||||
{ target: "stopSvc" }
|
||||
);
|
||||
if (ret.success) {
|
||||
lifecycleStatus.svcRunning = false;
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"success",
|
||||
"PLS 已停止",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
2000
|
||||
);
|
||||
} else {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
"error",
|
||||
"PLS 停止失败",
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -35,33 +257,60 @@ if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
|
||||
return true;
|
||||
},
|
||||
|
||||
updateStatus: async () => {
|
||||
updateStatusContent: async () => {
|
||||
const curPlsStats = await updatePlsStatusFromLocal();
|
||||
|
||||
const acIdInst = "acs-bc-psp-installStatus-container";
|
||||
const atIdInst = "acs-bc-psp-installStatus-text";
|
||||
switch (curPlsStats.installed) {
|
||||
switch (lifecycleStatus.installed) {
|
||||
case true:
|
||||
updateStatusEl(acIdInst, atIdInst, "SUCCESS", "已安装");
|
||||
if (!lifecycleStatus.svcInstalled) {
|
||||
updateStatusEl(acIdInst, atIdInst, "WARNING", "已下载, 服务未安装");
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", false);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Uninstall", true);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
|
||||
} else {
|
||||
updateStatusEl(acIdInst, atIdInst, "SUCCESS", "已安装");
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", true);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Uninstall", false);
|
||||
}
|
||||
break;
|
||||
case false:
|
||||
updateStatusEl(acIdInst, atIdInst, "PENDING", "未安装");
|
||||
updateStatusEl(acIdInst, atIdInst, "PENDING", "未下载");
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", true);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Uninstall", true);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
|
||||
}
|
||||
|
||||
const acIdLaunch = "acs-bc-psp-launchStatus-container";
|
||||
const atIdLaunch = "acs-bc-psp-launchStatus-text";
|
||||
switch (curPlsStats.launched) {
|
||||
|
||||
if (lifecycleStatus.detached) {
|
||||
updateStatusEl(acIdLaunch, atIdLaunch, "INFO", "已分离");
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
|
||||
} else if (lifecycleStatus.svcInstalled && lifecycleStatus.installed) {
|
||||
switch (lifecycleStatus.svcRunning || curPlsStats.launched) {
|
||||
case true:
|
||||
if (curPlsStats.status !== "notReady") {
|
||||
updateStatusEl(acIdLaunch, atIdLaunch, "SUCCESS", "已启动");
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", false);
|
||||
} else {
|
||||
updateStatusEl(acIdLaunch, atIdLaunch, "WARNING", "启动中");
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", false);
|
||||
}
|
||||
break;
|
||||
case false:
|
||||
updateStatusEl(acIdLaunch, atIdLaunch, "PENDING", "未启动");
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", false);
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const acIdConn = "acs-bc-psp-connStatus-container";
|
||||
const atIdConn = "acs-bc-psp-connStatus-text";
|
||||
@@ -70,7 +319,11 @@ if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
|
||||
updateStatusEl(acIdConn, atIdConn, "SUCCESS", "已连接");
|
||||
break;
|
||||
case false:
|
||||
if (curPlsStats.status !== "notReady") {
|
||||
updateStatusEl(acIdConn, atIdConn, "FAILED", "连接失败");
|
||||
} else {
|
||||
updateStatusEl(acIdConn, atIdConn, "PENDING", "等待启动");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -82,7 +335,53 @@ if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
|
||||
}
|
||||
},
|
||||
|
||||
refreshPlsStatus: async () => {
|
||||
refreshPlsStatus: async (init = false) => {
|
||||
const binExistsRet = await ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.getPlsBinExists`
|
||||
);
|
||||
if (binExistsRet.success && binExistsRet.data.isExists) {
|
||||
lifecycleStatus.installed = true;
|
||||
} else {
|
||||
lifecycleStatus.installed = false;
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
|
||||
return;
|
||||
}
|
||||
|
||||
const isDetachedRet = await ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.plsLifecycleQuery`,
|
||||
{ target: "isDetached" }
|
||||
);
|
||||
if (isDetachedRet.success && isDetachedRet.result) {
|
||||
lifecycleStatus.detached = true;
|
||||
} else {
|
||||
lifecycleStatus.detached = false;
|
||||
}
|
||||
|
||||
const isSvcInstalledRet = await ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.plsLifecycleQuery`,
|
||||
{ target: "isSvcInstalled" }
|
||||
);
|
||||
if (isSvcInstalledRet.success && isSvcInstalledRet.result) {
|
||||
lifecycleStatus.svcInstalled = true;
|
||||
} else {
|
||||
lifecycleStatus.svcInstalled = false;
|
||||
}
|
||||
|
||||
const isSvcRunningRet = await ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.plsLifecycleQuery`,
|
||||
{ target: "isSvcStart" }
|
||||
);
|
||||
if (isSvcRunningRet.success && isSvcRunningRet.result) {
|
||||
lifecycleStatus.svcRunning = true;
|
||||
} else {
|
||||
lifecycleStatus.svcRunning = false;
|
||||
}
|
||||
|
||||
if (init) {
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
|
||||
return;
|
||||
}
|
||||
|
||||
const updateOperationBtnStatus =
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus
|
||||
.updateOperationBtnStatus;
|
||||
@@ -95,10 +394,18 @@ if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
|
||||
|
||||
ipcRenderer.once(
|
||||
`${IPC_METHOD_BASE}.post.updateRetryStatus`,
|
||||
async (_evt, _arg) => {
|
||||
async (_evt, arg) => {
|
||||
await global.__HUGO_AURA_GLOBAL__.utils.sleep(50);
|
||||
updateOperationBtnStatus("Refresh", false, "刷新状态");
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatus();
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
|
||||
arg.success ? "success" : "error",
|
||||
arg.success ? "更新成功" : "连接失败",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
3000
|
||||
);
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
|
||||
}
|
||||
);
|
||||
} else if (result.success && result.status === "Already") {
|
||||
@@ -147,6 +454,9 @@ if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
|
||||
areaContainerEl.className =
|
||||
"acs-bc-pls-status-page-status-area warning";
|
||||
break;
|
||||
case "INFO":
|
||||
areaContainerEl.className = "acs-bc-pls-status-page-status-area info";
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -155,11 +465,11 @@ if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
|
||||
};
|
||||
|
||||
const onMounted = () => {
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
|
||||
initBsTooltip();
|
||||
GLOBAL_FUNCTIONS.updateStatus();
|
||||
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
|
||||
GLOBAL_FUNCTIONS.refreshPlsStatus(true);
|
||||
document.addEventListener("onPLSStatsUpdate", () => {
|
||||
GLOBAL_FUNCTIONS.updateStatus();
|
||||
GLOBAL_FUNCTIONS.updateStatusContent();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -4,53 +4,48 @@ const {
|
||||
updatePlsConfigToRemote,
|
||||
} = require(`${REQUIRE_BASE}/../../../../composables/plsConfigManager`);
|
||||
|
||||
const reusableChkFn = {
|
||||
checkRelativePath: () => {
|
||||
if (newVal === "" || !newVal)
|
||||
return { valid: false, hint: "请输入证书路径" };
|
||||
|
||||
if (newVal.includes(":/") || newVal.includes(":\\")) {
|
||||
return { valid: false, hint: "请输入相对路径, 而非绝对路径" };
|
||||
}
|
||||
|
||||
if (newVal.includes("\\")) {
|
||||
return {
|
||||
valid: false,
|
||||
hint: '请输入正确的路径, 使用 "/" 作为路径符',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const basicSettings = [
|
||||
{
|
||||
id: 0,
|
||||
categoryName: "可访问性",
|
||||
child: [
|
||||
/*
|
||||
{
|
||||
index: 0,
|
||||
id: "authToken",
|
||||
type: "input",
|
||||
subType: "text",
|
||||
name: "WebSocket 认证密钥",
|
||||
description: "选择一个安全的密钥, 用于 PLS 侧验证 Aura 前端身份",
|
||||
restart: true,
|
||||
reload: false,
|
||||
restartPLS: true,
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: "",
|
||||
placeHolder: "输入一个密钥",
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.plsToken;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (newVal === "" || !newVal)
|
||||
return { valid: false, hint: "请输入认证密钥" };
|
||||
|
||||
if (newVal.length < 8) {
|
||||
return { valid: false, hint: "至少输入 8 位字符" };
|
||||
}
|
||||
|
||||
global.__HUGO_AURA_CONFIG__.plsToken = newVal;
|
||||
return { valid: true };
|
||||
},
|
||||
},
|
||||
*/
|
||||
{
|
||||
index: 0,
|
||||
id: "plsListenPort",
|
||||
type: "input",
|
||||
subType: "text",
|
||||
name: "PLS WS 监听端口",
|
||||
description: "PLS 的 WebSocket 服务器将监听指定的端口",
|
||||
subType: "number",
|
||||
name: "PLS WS 默认监听端口",
|
||||
description: "PLS 的 WebSocket 服务器将默认监听指定的端口",
|
||||
reactive: true,
|
||||
reactiveVal: ["root.settings"],
|
||||
restart: false,
|
||||
reload: false,
|
||||
PLSRequired: true,
|
||||
restartPLS: true,
|
||||
restartPLS: false,
|
||||
warning: true,
|
||||
warningContent: "PLS 仍会在默认端口被占用时, 自动随机端口重试",
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: "",
|
||||
@@ -64,14 +59,114 @@ const basicSettings = [
|
||||
return { valid: false, hint: "请输入端口号" };
|
||||
|
||||
const numberNewVal = Number(newVal);
|
||||
if (numberNewVal === NaN || !(10000 <= numberNewVal <= 65535)) {
|
||||
if (numberNewVal === NaN || !(10000 <= numberNewVal) || !(newVal <= 65535)) {
|
||||
return { valid: false, hint: "请输入合法的端口号 (10000 ~ 65535)" };
|
||||
}
|
||||
|
||||
global.__HUGO_AURA__.plsSettings.wsPort = numberNewVal;
|
||||
updatePlsConfigToRemote("wsPort", numberNewVal);
|
||||
return { valid: true };
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
id: "plsCertPath",
|
||||
type: "input",
|
||||
subType: "text",
|
||||
name: "WSS TLS 证书相对路径",
|
||||
description: "PLS 将使用指定路径下的证书启动 WSS 服务器",
|
||||
reactive: true,
|
||||
reactiveVal: ["root.settings"],
|
||||
restart: false,
|
||||
reload: false,
|
||||
PLSRequired: true,
|
||||
restartPLS: true,
|
||||
tip: true,
|
||||
tipTitle:
|
||||
'路径相对于 "%USERPROFILE%\\Documents\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: "",
|
||||
placeHolder: "输入相对路径, 例如: config/vme50/cert.crt",
|
||||
valueGetter: () => {
|
||||
if (!global.__HUGO_AURA__.plsSettings) return "";
|
||||
return global.__HUGO_AURA__.plsSettings.certPath;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
const validate = reusableChkFn.checkRelativePath();
|
||||
if (!validate.valid) {
|
||||
return validate;
|
||||
}
|
||||
|
||||
global.__HUGO_AURA__.plsSettings.certPath = newVal;
|
||||
updatePlsConfigToRemote("certPath", newVal);
|
||||
return { valid: true };
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
id: "plsCertPath",
|
||||
type: "input",
|
||||
subType: "text",
|
||||
name: "WSS TLS 证书私钥相对路径",
|
||||
description: "PLS 将使用指定路径下的私钥启动 WSS 服务器",
|
||||
reactive: true,
|
||||
reactiveVal: ["root.settings"],
|
||||
restart: false,
|
||||
reload: false,
|
||||
PLSRequired: true,
|
||||
restartPLS: true,
|
||||
tip: true,
|
||||
tipTitle:
|
||||
'路径相对于 "%USERPROFILE%\\Documents\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
|
||||
warning: true,
|
||||
warningContent: "请使用 PEM 格式的密钥",
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: "",
|
||||
placeHolder: "输入相对路径, 例如: config/vme50/cert.key",
|
||||
valueGetter: () => {
|
||||
if (!global.__HUGO_AURA__.plsSettings) return "";
|
||||
return global.__HUGO_AURA__.plsSettings.keyPath;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
const validate = reusableChkFn.checkRelativePath();
|
||||
if (!validate.valid) {
|
||||
return validate;
|
||||
}
|
||||
|
||||
global.__HUGO_AURA__.plsSettings.keyPath = newVal;
|
||||
updatePlsConfigToRemote("keyPath", newVal);
|
||||
return { valid: true };
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
id: "plsRegenCertAftRelaunch",
|
||||
type: "switch",
|
||||
name: "重新生成 TLS 证书",
|
||||
description: "PLS 将在下次启动时重新生成 TLS 证书",
|
||||
reactive: true,
|
||||
reactiveVal: ["root.settings"],
|
||||
restart: false,
|
||||
reload: false,
|
||||
PLSRequired: true,
|
||||
restartPLS: true,
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
if (!global.__HUGO_AURA__.plsSettings) return "";
|
||||
return global.__HUGO_AURA__.plsSettings.regenCert;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (typeof newVal !== "boolean") return false;
|
||||
|
||||
global.__HUGO_AURA__.plsSettings.regenCert = newVal;
|
||||
updatePlsConfigToRemote("regenCert", newVal);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -30,7 +30,7 @@ const functions = {
|
||||
);
|
||||
break;
|
||||
case "update":
|
||||
const result = global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
|
||||
const result = await global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
|
||||
global.__HUGO_AURA_CONFIG__,
|
||||
null
|
||||
);
|
||||
@@ -179,7 +179,7 @@ const auraSettings = [
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings
|
||||
.settingsPasswordEnabled;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
callbackFn: async (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordEnabled =
|
||||
newVal;
|
||||
@@ -192,7 +192,7 @@ const auraSettings = [
|
||||
!newVal &&
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig
|
||||
) {
|
||||
global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
|
||||
await global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
|
||||
global.__HUGO_AURA_CONFIG__,
|
||||
null
|
||||
);
|
||||
@@ -220,13 +220,13 @@ const auraSettings = [
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
callbackFn: async (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.encryptConfig = newVal;
|
||||
if (newVal) {
|
||||
functions.handleEnableConfigEncryption("enc", null);
|
||||
} else {
|
||||
global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
|
||||
await global.__HUGO_AURA_CONFIG_MGR__.switchToDecConfig(
|
||||
global.__HUGO_AURA_CONFIG__,
|
||||
null
|
||||
);
|
||||
|
||||
@@ -11,8 +11,10 @@ const basicRouteHandler = (parsedWsMsg) => {
|
||||
const target = parsedWsMsg.type.split(".").slice(-1)[0];
|
||||
switch (target) {
|
||||
case "pushPlsInfo":
|
||||
if (global.__HUGO_AURA__.plsStats) {
|
||||
global.__HUGO_AURA__.plsStats.status = parsedWsMsg.data.status;
|
||||
global.__HUGO_AURA__.plsStats.version = parsedWsMsg.data.version;
|
||||
}
|
||||
|
||||
global.ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.updatePlsStats`,
|
||||
@@ -24,6 +26,19 @@ const basicRouteHandler = (parsedWsMsg) => {
|
||||
global.__HUGO_AURA__.plsStats
|
||||
);
|
||||
break;
|
||||
|
||||
case "plsNotReadyError":
|
||||
if (global.__HUGO_AURA__.plsStats) {
|
||||
global.__HUGO_AURA__.plsStats.launched = true;
|
||||
global.__HUGO_AURA__.plsStats.connected = false;
|
||||
global.__HUGO_AURA__.plsStats.status = "notReady";
|
||||
}
|
||||
|
||||
global.ipcRenderer.invoke(
|
||||
`${IPC_METHOD_BASE}.updatePlsStats`,
|
||||
global.__HUGO_AURA__.plsStats
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user