[🔄 Chore] Prepare for Aikari (2/2)

This commit is contained in:
Minoricew
2025-11-16 18:35:53 +08:00
committed by Minorice
parent 08290301a3
commit b277a61923
14 changed files with 586 additions and 333 deletions

View File

@@ -218,12 +218,17 @@ const applyAikariIpcHandler = (ipcMain) => {
const AIKARI_DEFAULT_INSTALL_DIR = path.join(
"C:\\Program Files",
"HugoAura Aikari"
"HugoAura",
"Aikari"
);
const AIKARI_LAUNCHER_PATH = path.join(
AIKARI_DEFAULT_INSTALL_DIR,
"Aikari-Launcher.exe"
);
const AIKARI_UNINSTALLER_PATH = path.join(
AIKARI_DEFAULT_INSTALL_DIR,
"unins000.exe"
);
const AIKARI_SVC_NAME = "HugoAuraAikari";
const isAikariDetached = process.argv.includes("--aikari-detach");
@@ -502,27 +507,27 @@ const applyAikariIpcHandler = (ipcMain) => {
return await functions.execCommand(
logHeader,
AIKARI_LAUNCHER_PATH,
"--startup auto install"
"--service install"
);
case "uninstSvc": {
const result = await functions.execCommand(
logHeader,
AIKARI_LAUNCHER_PATH,
"remove"
"--service uninstall"
);
return result;
}
case "startSvc":
return await functions.execCommand(
logHeader,
AIKARI_LAUNCHER_PATH,
"start"
"sc.exe",
`start ${AIKARI_SVC_NAME}`
);
case "stopSvc": {
const result = await functions.execCommand(
logHeader,
AIKARI_LAUNCHER_PATH,
"stop"
"sc.exe",
`stop ${AIKARI_SVC_NAME}`
);
if (result.success && global.__HUGO_AURA__.aikariStats) {
global.__HUGO_AURA__.aikariStats.connected = false;
@@ -547,7 +552,11 @@ const applyAikariIpcHandler = (ipcMain) => {
case "inst":
// TODO: Impl Aikari INST
case "uninst":
// same
return await functions.execCommand(
logHeader,
AIKARI_UNINSTALLER_PATH,
""
);
default:
return { success: false, errorObj: new Error("Method not found") };
}

View File

@@ -54,6 +54,29 @@ const actions = {
return null;
}
},
getAikariPLSRules: async (wsObj) => {
const eventId = genRandomHex();
wsObj.send(
JSON.stringify({
module: "pls",
eventId,
method: "config.rules.getConfig",
})
);
const promise = new Promise((resolve) => {
wsGetCallbacks.set(eventId, resolve);
});
const data = await promise;
if (data.success) {
console.debug(
"[HugoAura / UI / Aikari OCMS] Received Aikari PLS rules: ",
data
);
return data.data;
} else {
return null;
}
},
};
const onAikariConnectedMsgSeq = async ({ curAikariStates, wsObj }) => {
@@ -66,6 +89,7 @@ const onAikariConnectedMsgSeq = async ({ curAikariStates, wsObj }) => {
document.addEventListener("onAikariMessageRecv", onMsgRecvListener);
// Get Aikari Version
await actions.getAikariVersion(updatedAikariStates, wsObj);
// Get Aikari Launcher Config
const aikariLauncherConfig = await actions.getAikariLauncherConfig(wsObj);
if (aikariLauncherConfig) {
global.ipcRenderer.invoke(
@@ -73,6 +97,14 @@ const onAikariConnectedMsgSeq = async ({ curAikariStates, wsObj }) => {
aikariLauncherConfig
);
}
// Get Aikari PLS Rules
const aikariPLSRules = await actions.getAikariPLSRules(wsObj);
if (aikariPLSRules) {
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updateAikariRules`,
aikariPLSRules
);
}
return updatedAikariStates;
};

View File

@@ -35,7 +35,12 @@ const { genRandomHex } = require("../../utils/crypto");
* @param {string} configKey
* @param {any} configValue
*/
const updateAikariConfigToRemote = async (configKey, configValue) => {
const updateAikariConfigToRemote = async (
configKey,
configValue,
module = "launcher",
writeToDisk = true
) => {
const configLevels = configKey.split(".");
const aikariConfigUpdateEvent = new CustomEvent("onAikariConfigUpdate", {
@@ -57,18 +62,65 @@ const updateAikariConfigToRemote = async (configKey, configValue) => {
/**
* @type {ClientAikariRequest}
*/
/*
const data = {
method: "config.action.updateConfig",
method: "config.actions.updateConfig",
data: {
key: configKey,
value: configValue,
write: writeToDisk,
},
eventId: genRandomHex(), // 不用 crypto, 因为会带来不必要的性能开销
};*/
// TODO: Impl this ↑
module: module,
};
// global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.ws.sendWsMessage`, data);
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.ws.sendWsMessage`, data);
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.syncAikariConfig`, {
basic: global.__HUGO_AURA__.aikariSettings,
rules: global.__HUGO_AURA__.aikariRules,
});
};
// [!] Will be deprecated
const updateAikariPLSRulesToRemote = async (
configKey,
configValue,
affiliated,
writeToDisk = true
) => {
const configLevels = configKey.split(".");
const aikariRuleConfigUpdateEvent = new CustomEvent("onAikariConfigUpdate", {
detail: {
path: configLevels,
value: configValue,
},
});
document.dispatchEvent(aikariRuleConfigUpdateEvent);
const settingsEntries = document.getElementsByClassName(
"aura-settings-entry"
);
if (settingsEntries.length > 0) {
Array.from(settingsEntries).forEach((entry) => {
entry.dispatchEvent(aikariRuleConfigUpdateEvent);
});
}
/**
* @type {ClientAikariRequest}
*/
const data = {
method: "config.rules.updateConfig",
data: {
key: configKey,
value: configValue,
write: writeToDisk,
affiliated,
},
eventId: genRandomHex(),
module: "pls",
};
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.ws.sendWsMessage`, data);
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.syncAikariConfig`, {
basic: global.__HUGO_AURA__.aikariSettings,
rules: global.__HUGO_AURA__.aikariRules,
@@ -80,4 +132,5 @@ module.exports = {
updateAikariStatusFromLocal,
updateAikariSettingsFromLocal,
updateAikariConfigToRemote,
updateAikariPLSRulesToRemote,
};

View File

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

View File

@@ -50,9 +50,9 @@ const setDisableStatus = (el, isDisable, hint = null) => {
tooltipIns.enable();
}
} else {
el.setAttribute("data-bs-toggle", "tooltip");
el.setAttribute("data-bs-placement", "top");
el.setAttribute("data-bs-title", "None");
el.removeAttribute("data-bs-toggle");
el.removeAttribute("data-bs-placement");
el.removeAttribute("data-bs-title");
el.classList.remove("ase-operation-area-disabled");
const tooltipIns = bootstrap.Tooltip.getInstance(el);
if (tooltipIns) {
@@ -100,7 +100,18 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
case "radio": {
const elValue = entry.valueGetter();
const elArr = [];
for (const template of entry.templates) {
let targetTemplatesArr = [];
let targetTemplateLablesArr = [];
targetTemplatesArr =
typeof entry.templates === "function"
? entry.templates()
: entry.templates;
targetTemplateLablesArr =
typeof entry.templateLabels === "function"
? entry.templateLabels()
: entry.templateLabels;
for (const template of targetTemplatesArr) {
const inlineContainerEl = document.createElement("div");
inlineContainerEl.classList.add("form-check", "form-check-inline");
const radioEl = document.createElement("input");
@@ -108,7 +119,7 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
radioEl.classList.add("form-check-input");
radioEl.type = "radio";
radioEl.name = `${entry.id}Radios`;
radioEl.id = `${entry.id}Radio${entry.templates.indexOf(template)}`;
radioEl.id = `${entry.id}Radio${targetTemplatesArr.indexOf(template)}`;
radioEl.checked = template === elValue ? true : false;
radioEl.addEventListener("change", async (event) => {
if (event.target.checked) {
@@ -126,7 +137,53 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
labelEl.classList.add("form-check-label");
labelEl.setAttribute("for", radioEl.id);
labelEl.textContent =
entry.templateLabels[entry.templates.indexOf(template)];
targetTemplateLablesArr[targetTemplatesArr.indexOf(template)];
inlineContainerEl.appendChild(labelEl);
elArr.push(inlineContainerEl);
}
return elArr;
}
case "checkbox": {
const elValue = entry.valueGetter();
const elArr = [];
let targetTemplatesArr = [];
let targetTemplateLablesArr = [];
targetTemplatesArr =
typeof entry.templates === "function"
? entry.templates()
: entry.templates;
targetTemplateLablesArr =
typeof entry.templateLabels === "function"
? entry.templateLabels()
: entry.templateLabels;
for (const templateName of targetTemplatesArr) {
const inlineContainerEl = document.createElement("div");
inlineContainerEl.classList.add("form-check", "form-check-inline");
const chkBoxEl = document.createElement("input");
chkBoxEl.value = templateName;
chkBoxEl.classList.add("form-check-input");
chkBoxEl.type = "checkbox";
chkBoxEl.name = `${entry.id}Checkbox`;
chkBoxEl.id = `${entry.id}Checkbox${targetTemplatesArr.indexOf(
templateName
)}`;
chkBoxEl.checked = elValue.includes(templateName) ? true : false;
chkBoxEl.addEventListener("change", async (event) => {
showToast(entry);
await entry.callbackFn(
event.target.value,
chkBoxEl,
operationArea,
descriptionArea
);
});
inlineContainerEl.appendChild(chkBoxEl);
const labelEl = document.createElement("label");
labelEl.classList.add("form-check-label");
labelEl.setAttribute("for", chkBoxEl.id);
labelEl.textContent =
targetTemplateLablesArr[targetTemplatesArr.indexOf(templateName)];
inlineContainerEl.appendChild(labelEl);
elArr.push(inlineContainerEl);
}
@@ -219,7 +276,7 @@ const renderNormalSettingsItem = (entry, formEl) => {
powerIcon.setAttribute("data-bs-title", "需要重启 Electron 进程");
entryTitle.appendChild(powerIcon);
}
if (entry.AikariRequired) {
if (entry.aikariRequired) {
const aikariIcon = document.createElement("i");
aikariIcon.classList.add(
"layui-icon",
@@ -302,14 +359,14 @@ const renderNormalSettingsItem = (entry, formEl) => {
insertOrRemoveEl(entryOperationArea, targetEl, true);
}
};
const channel = entry.AikariRequired
const channel = entry.aikariRequired
? "onAikariConfigUpdate"
: "onHugoAuraConfigUpdate";
entryContainerEl.addEventListener(channel, evtListener);
// createOnLeaveEvtListener(channel, evtListener);
}
if (entry.AikariRequired) {
if (entry.aikariRequired) {
if (!global.__HUGO_AURA__.aikariStats.connected) {
setDisableStatus(entryOperationArea, true, "连接至 Aikari 以继续");
}
@@ -332,7 +389,7 @@ const renderNormalSettingsItem = (entry, formEl) => {
const isDisabledRet = entry.auraDisable();
setDisableStatus(
entryOperationArea,
isDisabledRet.value,
global.__HUGO_AURA__.aikariStats.connected ? isDisabledRet.value : true,
isDisabledRet.tooltip
);
};
@@ -354,7 +411,7 @@ const renderNormalSettingsItem = (entry, formEl) => {
updateDisableStatus();
}
};
const channel = entry.AikariRequired
const channel = entry.aikariRequired
? "onAikariConfigUpdate"
: "onHugoAuraConfigUpdate";
entryContainerEl.addEventListener(channel, evtListener);
@@ -397,7 +454,9 @@ const renderPreviewItem = (entry, formEl) => {
};
document.addEventListener(
eventChannel === "pls" ? "onAikariConfigUpdate" : "onHugoAuraConfigUpdate",
eventChannel === "aikari"
? "onAikariConfigUpdate"
: "onHugoAuraConfigUpdate",
eventListener
);
createOnLeaveEvtListener(eventListener); // Clean up

View File

@@ -6,9 +6,11 @@
let tooltipTriggerCache = null;
const refreshBsTooltip = (selector = '[data-bs-toggle="tooltip"]') => {
if (tooltipTriggerCache) {
[...tooltipTriggerCache].map((el) =>
bootstrap.Tooltip.getInstance(el).disable()
);
[...tooltipTriggerCache].map((el) => {
if (bootstrap.Tooltip.getInstance(el)) {
bootstrap.Tooltip.getInstance(el).disable();
}
});
}
const tooltipTriggerList = document.querySelectorAll(selector);

View File

@@ -27,7 +27,7 @@
<div
class="acs-bc-psp-operation-btn"
aura-disabled="true"
id="acsBcPsp-operBtn-Download"
id="acsBcPsp-operBtn-Install"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -44,10 +44,10 @@
d="M17 26.17V14h-2v12.17l-2.59-2.58L11 25l5 5l5-5l-1.41-1.41z"
/>
</svg>
<p>下载内核</p>
<p>下载应用</p>
</div>
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Install">
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-InstallSvc">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
@@ -72,7 +72,7 @@
<div
class="acs-bc-psp-operation-btn acs-bc-psp-o-btn-dangerous"
id="acsBcPsp-operBtn-Uninstall"
id="acsBcPsp-operBtn-UninstallSvc"
>
<svg
xmlns="http://www.w3.org/2000/svg"

View File

@@ -33,7 +33,9 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
const toastHeaderEl = document.getElementById(
"aikariStatusNotifyToastTitle"
);
const toastBodyEl = document.getElementById("aikariStatusNotifyToastBody");
const toastBodyEl = document.getElementById(
"aikariStatusNotifyToastBody"
);
const bsToastIns = bootstrap.Toast.getOrCreateInstance(toastRootEl);
if (bsToastIns.isShown) {
@@ -96,7 +98,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.refreshAikariStatus();
};
break;
case "Download":
case "Install":
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.downloadAndInstallAikariBin();
};
@@ -104,10 +106,10 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
// ↓ 这边的确可以把这些全都合并到一个可复用 fn 里去, 但没必要
// 如果后续要引入错误视觉反馈, 合并到单个 fn 反而会增加实现复杂度
case "Install":
case "InstallSvc":
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateOperationBtnStatus(
"Install",
"InstallSvc",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateToast(
@@ -145,24 +147,26 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateStatusContent();
};
break;
case "Uninstall":
if (btnContent === "删除内核") {
case "UninstallSvc":
if (btnContent === "卸载应用") {
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateOperationBtnStatus(
"Uninstall",
"UninstallSvc",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateToast(
"warning",
"正在删除内核",
null,
"正在卸载 Aikari",
`<p>
请在弹出窗口中完成操作
</p>`,
false,
false,
null
);
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.aikariLifecycleControl`,
{ target: "rmBin" }
{ target: "uninst" }
);
if (ret.success) {
lifecycleStatus.installed = false;
@@ -176,7 +180,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateToast(
"success",
"内核已删除",
"Aikari 已完成卸载",
null,
true,
true,
@@ -185,7 +189,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateToast(
"error",
"内核删除失败",
"Aikari 卸载失败",
`<p>
${ret.errorObj ? ret.errorObj : "检查日志以获取详细信息"}
</p>`,
@@ -199,7 +203,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
} else {
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateOperationBtnStatus(
"Uninstall",
"UninstallSvc",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateToast(
@@ -239,7 +243,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
}
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.aikariLifecycleControl`,
{ target: "rmSvc" }
{ target: "uninstSvc" }
);
if (ret.success) {
lifecycleStatus.svcInstalled = false;
@@ -296,7 +300,9 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
2000
);
await global.__HUGO_AURA_GLOBAL__.utils.sleep(100);
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.retryAikariConnect`);
await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.retryAikariConnect`
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateStatusContent();
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.aikariStatus.updateStatusContent();
@@ -375,20 +381,25 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
switch (lifecycleStatus.installed) {
case true:
if (!lifecycleStatus.svcInstalled) {
updateStatusEl(acIdInst, atIdInst, "WARNING", "已下载, 服务未安装");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", false);
updateStatusEl(
acIdInst,
atIdInst,
"WARNING",
"应用已安装, 服务未安装"
);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("InstallSvc", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Uninstall",
"UninstallSvc",
false,
"删除内核"
"卸载应用"
);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
} else {
updateStatusEl(acIdInst, atIdInst, "SUCCESS", "已安装");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("InstallSvc", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Uninstall",
"UninstallSvc",
false,
"卸载服务"
);
@@ -396,9 +407,9 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
break;
case false:
updateStatusEl(acIdInst, atIdInst, "PENDING", "未下载");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Uninstall", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("InstallSvc", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("UninstallSvc", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
}
@@ -450,7 +461,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
if (curAikariStats.version && curAikariStats.version !== "unknown") {
versionTextEl.textContent = curAikariStats.version;
} else {
versionTextEl.textContent = "不可用"
versionTextEl.textContent = "不可用";
}
},
@@ -468,7 +479,10 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
const binExistsRet = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.getIfAikariBinExists`
);
if ((binExistsRet.success && binExistsRet.data.isExists) || lifecycleStatus.detached) {
if (
(binExistsRet.success && binExistsRet.data.isExists) ||
lifecycleStatus.detached
) {
lifecycleStatus.installed = true;
global.__HUGO_AURA__.aikariStats.installed = true;
ipcRenderer.invoke(
@@ -628,7 +642,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
const CUR_CHANNEL = `${IPC_METHOD_BASE}.post.reportAikariInstallStep`;
if (!retrieveMode) {
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "正在检查");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", true, "正在检查");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", true);
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.ensureAikariInstallDir`);
GLOBAL_FUNCTIONS.updateToast(
@@ -639,7 +653,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
true,
2000
);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", true);
} else {
GLOBAL_FUNCTIONS.updateToast(
"info",
@@ -683,9 +697,9 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
"Install",
false,
"下载内核"
"下载应用"
);
break;
case "done":
@@ -711,9 +725,9 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
"Install",
true,
"下载内核"
"下载应用"
);
lifecycleStatus.installed = true;
global.__HUGO_AURA__.aikariStats.installed = true;
@@ -756,9 +770,9 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
case "cancelled":
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
"Install",
false,
"下载内核"
"下载应用"
);
break;
}
@@ -852,14 +866,16 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
"acs-bc-aikari-status-page-status-area success";
break;
case "FAILED":
areaContainerEl.className = "acs-bc-aikari-status-page-status-area failed";
areaContainerEl.className =
"acs-bc-aikari-status-page-status-area failed";
break;
case "WARNING":
areaContainerEl.className =
"acs-bc-aikari-status-page-status-area warning";
break;
case "INFO":
areaContainerEl.className = "acs-bc-aikari-status-page-status-area info";
areaContainerEl.className =
"acs-bc-aikari-status-page-status-area info";
break;
default:
return false;

View File

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

View File

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

View File

@@ -4,28 +4,6 @@ const {
updateAikariConfigToRemote,
} = require(`${REQUIRE_BASE}/../../../../composables/aikariConfigManager`);
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,
@@ -33,137 +11,70 @@ const basicSettings = [
child: [
{
index: 0,
id: "plsListenPort",
id: "aikarWsPreferPort",
type: "input",
subType: "number",
name: "PLS WS 默认监听端口",
description: "PLS 的 WebSocket 服务器默认监听指定的端口",
name: "Aikari WS 默认监听端口",
description: "Aikari WebSocket 服务器默认监听的端口",
reactive: true,
reactiveVal: ["root.settings"],
restart: false,
reload: false,
AikariRequired: true,
aikariRequired: true,
restartAikari: false,
warning: true,
warningContent: "PLS 仍会在默认端口被占用时, 自动随机端口重试",
warningContent: "Aikari 仍会在默认端口被占用时, 自动随机端口重试",
associateVal: null,
auraIf: () => true,
defaultValue: "",
placeHolder: "输入端口号 (10000 ~ 65535)",
valueGetter: () => {
if (!global.__HUGO_AURA__.aikariSettings) return "";
return global.__HUGO_AURA__.aikariSettings.wsPort;
return global.__HUGO_AURA__.aikariSettings.wsPreferPort;
},
callbackFn: (newVal) => {
if (newVal === "" || !newVal)
return { valid: false, hint: "请输入端口号" };
const numberNewVal = Number(newVal);
if (numberNewVal === NaN || !(10000 <= numberNewVal) || !(newVal <= 65535)) {
if (
numberNewVal === NaN ||
!(10000 <= numberNewVal) ||
!(newVal <= 65535)
) {
return { valid: false, hint: "请输入合法的端口号 (10000 ~ 65535)" };
}
global.__HUGO_AURA__.aikariSettings.wsPort = numberNewVal;
updateAikariConfigToRemote("wsPort", numberNewVal);
global.__HUGO_AURA__.aikariSettings.wsPreferPort = numberNewVal;
updateAikariConfigToRemote("wsPreferPort", numberNewVal);
return { valid: true };
},
},
{
index: 1,
id: "plsCertPath",
type: "input",
subType: "text",
name: "WSS TLS 证书相对路径",
description: "PLS 将使用指定路径下的证书启动 WSS 服务器",
reactive: true,
reactiveVal: ["root.settings"],
restart: false,
reload: false,
AikariRequired: true,
restartAikari: true,
tip: true,
tipTitle:
'路径相对于 "%PROGRAMDATA%\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
associateVal: null,
auraIf: () => true,
defaultValue: "",
placeHolder: "输入相对路径, 例如: config/vme50/cert.crt",
valueGetter: () => {
if (!global.__HUGO_AURA__.aikariSettings) return "";
return global.__HUGO_AURA__.aikariSettings.certPath;
},
callbackFn: (newVal) => {
const validate = reusableChkFn.checkRelativePath();
if (!validate.valid) {
return validate;
}
global.__HUGO_AURA__.aikariSettings.certPath = newVal;
updateAikariConfigToRemote("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,
AikariRequired: true,
restartAikari: true,
tip: true,
tipTitle:
'路径相对于 "%PROGRAMDATA%\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
warning: true,
warningContent: "请使用 PEM 格式的密钥",
associateVal: null,
auraIf: () => true,
defaultValue: "",
placeHolder: "输入相对路径, 例如: config/vme50/cert.key",
valueGetter: () => {
if (!global.__HUGO_AURA__.aikariSettings) return "";
return global.__HUGO_AURA__.aikariSettings.keyPath;
},
callbackFn: (newVal) => {
const validate = reusableChkFn.checkRelativePath();
if (!validate.valid) {
return validate;
}
global.__HUGO_AURA__.aikariSettings.keyPath = newVal;
updateAikariConfigToRemote("keyPath", newVal);
return { valid: true };
},
},
{
index: 3,
id: "plsRegenCertAftRelaunch",
id: "aikariForceRegenWsTlsCert",
type: "switch",
name: "重新生成 TLS 证书",
description: "PLS 将在下次启动时重新生成 TLS 证书",
name: "重新生成 WS TLS 证书",
description: "Aikari 将在下次启动时重新生成用于 WebSocket 的 TLS 证书",
reactive: true,
reactiveVal: ["root.settings"],
restart: false,
reload: false,
AikariRequired: true,
aikariRequired: true,
restartAikari: true,
associateVal: null,
auraIf: () => true,
defaultValue: false,
valueGetter: () => {
if (!global.__HUGO_AURA__.aikariSettings) return "";
return global.__HUGO_AURA__.aikariSettings.regenCert;
return global.__HUGO_AURA__.aikariSettings.tls.regenWsCertNextLaunch;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return false;
global.__HUGO_AURA__.aikariSettings.regenCert = newVal;
updateAikariConfigToRemote("regenCert", newVal);
global.__HUGO_AURA__.aikariSettings.tls.regenWsCertNextLaunch =
newVal;
updateAikariConfigToRemote("tls.regenWsCertNextLaunch", newVal);
return true;
},
},

View File

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

View File

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

View File

@@ -36,8 +36,8 @@
resolve({
success: true,
data: null,
status: response.status
})
status: response.status,
});
}
const parsedData = await response.json();
@@ -84,7 +84,7 @@
const diskElTemplate = document.createElement("p");
diskElTemplate.classList.add("acs-bc-dsc-fop-disk-el");
if (!curConfig.enable) {
if (!curConfig.enabled) {
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
if (disk.status !== 0) {
@@ -95,44 +95,16 @@
diskContainerEl.appendChild(curDiskEl);
}
} else {
switch (curConfig.rewriteMode) {
case "allFreeze":
{
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
// @ts-expect-error
curDiskEl.classList.add("active");
curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl);
}
}
break;
case "systemOnly":
{
let idx = 0;
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
// @ts-expect-error
if (idx === 0) curDiskEl.classList.add("active");
curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl);
idx += 1;
}
}
break;
case "exceptSecondDisk":
{
let idx = 0;
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
// @ts-expect-error
if (idx === 0) curDiskEl.classList.add("active");
curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl);
idx += 1;
}
}
break;
let idx = 0;
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
if (curConfig.frozenDisks.includes(disk.name.toLowerCase())) {
// @ts-expect-error
curDiskEl.classList.add("active");
}
curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl);
idx += 1;
}
}
@@ -148,10 +120,10 @@
)[0];
const eventListener = (_event) => {
// if (!global.__HUGO_AURA__.plsRules) return;
if (!global.__HUGO_AURA__.aikariRules) return;
composables.getAndUpdateDiskInfo(
// global.__HUGO_AURA__.plsRules.client.security.uploadFreezeInfo
{ enable: false }
global.__HUGO_AURA__.aikariRules.ssaFeatures.securityPolicies
.freezeManagement.freezeDiskInfoPost
);
};
rootEl.addEventListener("onAssociateValueUpdated", eventListener);