mirror of
https://github.com/HugoAura/Seewo-HugoAura.git
synced 2026-06-20 23:14:28 +08:00
[Feat] Settings UI Auth (#3) & Prepare for v0.1.1-rel
This commit is contained in:
@@ -1,11 +1,34 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
// @ts-check
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
const crypto = require("crypto");
|
||||
const childProc = require("child_process");
|
||||
|
||||
// Constants
|
||||
|
||||
const CRYPTO_SETTINGS_AES = {
|
||||
mode: "aes-256-gcm",
|
||||
keyLength: 32,
|
||||
keyIter: 100000,
|
||||
ivLength: 12,
|
||||
tagLength: 16,
|
||||
saltLength: 16,
|
||||
obfuscateStr: "eCybsseK",
|
||||
hash: "sha256",
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Record<any, any>} target
|
||||
* @param {Record<any, any>} source
|
||||
* @returns
|
||||
*/
|
||||
const deepMerge = (target, source) => {
|
||||
const result = JSON.parse(JSON.stringify(target));
|
||||
|
||||
if (!source || typeof source !== 'object') {
|
||||
if (!source || typeof source !== "object") {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -15,9 +38,9 @@ const deepMerge = (target, source) => {
|
||||
if (!(key in source)) {
|
||||
keysToDelete.push(key);
|
||||
} else if (
|
||||
typeof result[key] === 'object' &&
|
||||
typeof result[key] === "object" &&
|
||||
result[key] !== null &&
|
||||
typeof source[key] === 'object' &&
|
||||
typeof source[key] === "object" &&
|
||||
source[key] !== null
|
||||
) {
|
||||
result[key] = deepMerge(result[key], source[key]);
|
||||
@@ -42,13 +65,11 @@ const deepMerge = (target, source) => {
|
||||
|
||||
class ConfigManager {
|
||||
constructor() {
|
||||
this.configPath = path.join(
|
||||
os.homedir(),
|
||||
'Documents',
|
||||
'HugoAura',
|
||||
'config.json'
|
||||
);
|
||||
this.defaultConfigPath = path.join(__dirname, 'default.json');
|
||||
this.configDir = path.join(os.homedir(), "Documents", "HugoAura");
|
||||
this.configPath = path.join(this.configDir, "config.json");
|
||||
this.encConfigPath = path.join(this.configDir, ".cache_2eafc8d0.dat"); // (雾
|
||||
/* ↑ 不使用 .tmp 扩展名, 不然容易真被清理了 */
|
||||
this.defaultConfigPath = path.join(__dirname, "default.json");
|
||||
}
|
||||
|
||||
getHugoAuraConfigPath() {
|
||||
@@ -61,10 +82,10 @@ class ConfigManager {
|
||||
|
||||
getDefaultConfig() {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(this.defaultConfigPath, 'utf8'));
|
||||
return JSON.parse(fs.readFileSync(this.defaultConfigPath, "utf8"));
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'[HugoAura / Config] No default config found, using empty config'
|
||||
"[HugoAura / Config] No default config found, using empty config"
|
||||
);
|
||||
return { rewrite: {} };
|
||||
}
|
||||
@@ -74,12 +95,12 @@ class ConfigManager {
|
||||
if (global.__HUGO_AURA__.configInit) return;
|
||||
const hugoAuraPath = this.getHugoAuraConfigPath();
|
||||
if (!fs.existsSync(hugoAuraPath)) {
|
||||
console.log('[HugoAura / Config] Creating HugoAura directory');
|
||||
console.log("[HugoAura / Config] Creating HugoAura directory");
|
||||
fs.mkdirSync(hugoAuraPath, { recursive: true });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(this.configPath)) {
|
||||
console.log('[HugoAura / Config] Creating default config file');
|
||||
console.log("[HugoAura / Config] Creating default config file");
|
||||
const defaultConfig = this.getDefaultConfig();
|
||||
this.writeConfig(defaultConfig);
|
||||
}
|
||||
@@ -87,25 +108,30 @@ class ConfigManager {
|
||||
|
||||
readConfig() {
|
||||
try {
|
||||
const config = JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
|
||||
console.log('[HugoAura / Config] Successfully loaded config:', config);
|
||||
const config = JSON.parse(fs.readFileSync(this.configPath, "utf8"));
|
||||
console.log("[HugoAura / Config] Successfully loaded config:", config);
|
||||
return config;
|
||||
} catch (err) {
|
||||
console.error('[HugoAura / Config] Failed to read config:', err);
|
||||
console.error("[HugoAura / Config] Failed to read config:", err);
|
||||
return this.getDefaultConfig();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Record<any, any>} config
|
||||
* @returns
|
||||
*/
|
||||
writeConfig(config) {
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
this.configPath,
|
||||
JSON.stringify(config, null, 2),
|
||||
'utf8'
|
||||
"utf8"
|
||||
);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('[HugoAura / Config] Failed to write config:', err);
|
||||
console.error("[HugoAura / Config] Failed to write config:", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -115,23 +141,177 @@ class ConfigManager {
|
||||
let config = {};
|
||||
try {
|
||||
if (fs.existsSync(this.configPath)) {
|
||||
const userConfig = JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
|
||||
const userConfig = JSON.parse(fs.readFileSync(this.configPath, "utf8"));
|
||||
if (global.__HUGO_AURA__.configInit) {
|
||||
config = userConfig;
|
||||
return userConfig;
|
||||
} else {
|
||||
config = deepMerge(userConfig, defaultConfig);
|
||||
console.log('[HugoAura / Config] Merged with user config');
|
||||
console.log("[HugoAura / Config] Merged with user config");
|
||||
this.writeConfig(config);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[HugoAura / Config] Failed to load user config:', err);
|
||||
console.error("[HugoAura / Config] Failed to load user config:", err);
|
||||
config = defaultConfig;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Record<any, any>} configData
|
||||
* @param {string} passwd
|
||||
*/
|
||||
encryptConfig(configData, passwd) {
|
||||
const salt = crypto.randomBytes(CRYPTO_SETTINGS_AES.saltLength);
|
||||
const key = crypto.pbkdf2Sync(
|
||||
passwd,
|
||||
salt,
|
||||
CRYPTO_SETTINGS_AES.keyIter,
|
||||
CRYPTO_SETTINGS_AES.keyLength,
|
||||
CRYPTO_SETTINGS_AES.hash
|
||||
);
|
||||
const iv = crypto.randomBytes(CRYPTO_SETTINGS_AES.ivLength);
|
||||
|
||||
const cipherIns = crypto.createCipheriv(CRYPTO_SETTINGS_AES.mode, key, iv, {
|
||||
// @ts-expect-error
|
||||
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
|
||||
});
|
||||
|
||||
const stringifyConfigData = JSON.stringify(configData);
|
||||
let encryptedCfg = cipherIns.update(stringifyConfigData, "utf-8", "hex");
|
||||
encryptedCfg += cipherIns.final("hex");
|
||||
const authTag = cipherIns.getAuthTag();
|
||||
|
||||
/** @type {EncryptedConfig} */
|
||||
const encConfigFinal = {};
|
||||
encConfigFinal.content = encryptedCfg;
|
||||
encConfigFinal.authTag = authTag.toString("base64");
|
||||
encConfigFinal.salt = salt.toString("base64");
|
||||
encConfigFinal.iv = iv.toString("base64");
|
||||
|
||||
const base64EncConfig =
|
||||
CRYPTO_SETTINGS_AES.obfuscateStr +
|
||||
Buffer.from(JSON.stringify(encConfigFinal)).toString("base64");
|
||||
|
||||
try {
|
||||
fs.writeFileSync(this.encConfigPath, base64EncConfig, "utf-8");
|
||||
// fs.rmSync(this.configPath);
|
||||
|
||||
const _hideFileProc = childProc.spawnSync(
|
||||
"cmd.exe",
|
||||
["/c", "attrib", "+h", this.encConfigPath],
|
||||
{
|
||||
stdio: "inherit",
|
||||
}
|
||||
);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to write encrypted config:",
|
||||
err
|
||||
);
|
||||
console.error(
|
||||
"[HugoAura / Config] Pending config data:",
|
||||
base64EncConfig
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} passwd
|
||||
* @returns
|
||||
*/
|
||||
decryptConfig(passwd) {
|
||||
const FAILED_RET = {
|
||||
success: false,
|
||||
data: {},
|
||||
};
|
||||
|
||||
let base64EncConfig = null;
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(this.encConfigPath)) {
|
||||
return FAILED_RET;
|
||||
}
|
||||
base64EncConfig = fs.readFileSync(this.encConfigPath, "utf-8");
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to read encrypted config:",
|
||||
err
|
||||
);
|
||||
return FAILED_RET;
|
||||
}
|
||||
|
||||
if (base64EncConfig) {
|
||||
const strip64EncCfg = base64EncConfig.split(
|
||||
CRYPTO_SETTINGS_AES.obfuscateStr
|
||||
)[1];
|
||||
const encryptCfg = Buffer.from(strip64EncCfg, "base64").toString("utf-8");
|
||||
/** @type {null | EncryptedConfig} */
|
||||
let parsedEncCfg = null;
|
||||
try {
|
||||
parsedEncCfg = JSON.parse(encryptCfg);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to parse encrypted config:",
|
||||
err
|
||||
);
|
||||
console.error("[HugoAura / Config] Pending data:", encryptCfg);
|
||||
}
|
||||
if (parsedEncCfg === null) return FAILED_RET;
|
||||
|
||||
const salt = Buffer.from(parsedEncCfg.salt, "base64");
|
||||
const iv = Buffer.from(parsedEncCfg.iv, "base64");
|
||||
|
||||
const authTag = Buffer.from(parsedEncCfg.authTag, "base64");
|
||||
|
||||
const key = crypto.pbkdf2Sync(
|
||||
passwd,
|
||||
salt,
|
||||
CRYPTO_SETTINGS_AES.keyIter,
|
||||
CRYPTO_SETTINGS_AES.keyLength,
|
||||
CRYPTO_SETTINGS_AES.hash
|
||||
);
|
||||
|
||||
const decipherIns = crypto.createDecipheriv(
|
||||
CRYPTO_SETTINGS_AES.mode,
|
||||
key,
|
||||
iv,
|
||||
{
|
||||
// @ts-expect-error
|
||||
authTagLength: CRYPTO_SETTINGS_AES.tagLength,
|
||||
}
|
||||
);
|
||||
|
||||
decipherIns.setAuthTag(authTag);
|
||||
|
||||
let stringifyDecCfg = Buffer.concat([
|
||||
decipherIns.update(parsedEncCfg.content, "hex"),
|
||||
decipherIns.final(),
|
||||
]).toString();
|
||||
|
||||
/** @type {null | Record<any, any>} */
|
||||
let decConfig = null;
|
||||
try {
|
||||
decConfig = JSON.parse(stringifyDecCfg);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"[HugoAura / Config] Failed to parse decrypted config:",
|
||||
err
|
||||
);
|
||||
console.error("[HugoAura / Config] Pending data:", decConfig);
|
||||
return FAILED_RET;
|
||||
}
|
||||
if (decConfig === null) return FAILED_RET;
|
||||
|
||||
console.debug(decConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ConfigManager();
|
||||
|
||||
@@ -19,5 +19,12 @@
|
||||
}
|
||||
},
|
||||
"plsToken": "66ccff0d000721114514191981023333",
|
||||
"auraSettings": {
|
||||
"settingsPasswordEnabled": false,
|
||||
"settingsPasswordWithSalt": "32703D292460CC9A3B867494D6AD9A8E4A3ADF0FAA4D6867BC4D412CC3927D02E47C6D0B1763BB53E57B2241C6193433561CDA09D7C48CA03983072B876F0965",
|
||||
"appearance": {
|
||||
"enablePasswdDialogBlur": true
|
||||
}
|
||||
},
|
||||
"devTools": false
|
||||
}
|
||||
|
||||
9
src/aura/types/shared/config.d.ts
vendored
Executable file
9
src/aura/types/shared/config.d.ts
vendored
Executable file
@@ -0,0 +1,9 @@
|
||||
type AES256EncryptedConfig = string;
|
||||
type Base64String = string;
|
||||
|
||||
interface EncryptedConfig {
|
||||
content: AES256EncryptedConfig;
|
||||
authTag: Base64String;
|
||||
salt: Base64String;
|
||||
iv: Base64String;
|
||||
}
|
||||
@@ -68,3 +68,29 @@
|
||||
.aura-settings-entry-property-icon.layui-icon-refresh {
|
||||
color: rgb(0, 106, 188);
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
||||
@keyframes invalidShake {
|
||||
0% {
|
||||
margin-left: calc(-10px * 2);
|
||||
}
|
||||
16% {
|
||||
margin-left: calc(9px * 2);
|
||||
}
|
||||
33% {
|
||||
margin-left: calc(-6px * 2);
|
||||
}
|
||||
50% {
|
||||
margin-left: calc(5px * 2);
|
||||
}
|
||||
66% {
|
||||
margin-left: calc(-2px * 2);
|
||||
}
|
||||
83% {
|
||||
margin-left: calc(1px * 2);
|
||||
}
|
||||
100% {
|
||||
margin-left: calc(0px * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
color: white;
|
||||
z-index: 12000;
|
||||
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
@@ -368,6 +369,82 @@
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
/* Auth Dialog */
|
||||
|
||||
.aura-config-page-auth-dialog-area {
|
||||
position: absolute;
|
||||
height: calc(100% - 40px);
|
||||
width: 100%;
|
||||
top: 40px;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10000;
|
||||
background-color: rgba(255, 255, 255, 0.35);
|
||||
|
||||
opacity: 1;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.aura-config-page-auth-dialog-area.blur-enabled {
|
||||
height: 100%;
|
||||
top: 0;
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(5px);
|
||||
filter: blur(0.1px);
|
||||
/* ↑ 似乎会导致性能问题 */
|
||||
}
|
||||
|
||||
.acp-ada-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.acp-ada-hidden.blur-enabled {
|
||||
backdrop-filter: blur(0.1px);
|
||||
filter: unset;
|
||||
}
|
||||
|
||||
.aura-config-page-auth-dialog {
|
||||
height: 40%;
|
||||
width: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.625);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.acp-auth-dialog-title {
|
||||
font-size: x-large;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
#acp-auth-user-input {
|
||||
max-width: 50%;
|
||||
/* background-color: rgba(255, 255, 255, 0.5); */
|
||||
border-radius: 35px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#acp-auth-user-input.invalid {
|
||||
animation: invalidShake 0.6s linear;
|
||||
}
|
||||
|
||||
.acp-auth-confirm-btn {
|
||||
background-color: transparent;
|
||||
border-radius: 35px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.acp-auth-confirm-btn .layui-icon {
|
||||
font-size: 24px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
/* Toast */
|
||||
|
||||
.aura-config-page-toast-area {
|
||||
|
||||
@@ -80,8 +80,10 @@
|
||||
</div>
|
||||
<div
|
||||
class="operation-el-hidden aura-config-page-operation-el"
|
||||
onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig('behaviourCtrl', true)"
|
||||
aura-disabled="true"
|
||||
>
|
||||
<!-- Still WIP -->
|
||||
<!-- onclick="window.__HUGO_AURA_UI_FUNCTIONS__.config.toggleSubConfig('behaviourCtrl', true)" -->
|
||||
<div class="aura-config-page-operation-body">
|
||||
<img src="../../aura/ui/static/config/behaviour_mon.svg" />
|
||||
<div>
|
||||
@@ -114,6 +116,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="aura-config-page-auth-dialog-area acp-ada-hidden"
|
||||
style="display: none"
|
||||
>
|
||||
<div class="aura-config-page-auth-dialog">
|
||||
<p class="acp-auth-dialog-title">验证您的身份</p>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
placeholder="请输入密码..."
|
||||
aria-label="Aura Password"
|
||||
id="acp-auth-user-input"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="acp-auth-confirm-btn"
|
||||
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.config.verifyAuthPassword()"
|
||||
>
|
||||
<i class="layui-icon layui-icon-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aura-config-page-toast-area">
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||
<div
|
||||
|
||||
@@ -117,6 +117,48 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
|
||||
|
||||
global.__HUGO_AURA_UI_REACTIVES__.config.isInSubPage = side;
|
||||
},
|
||||
|
||||
verifyAuthPassword: async () => {
|
||||
const showFailedAnimation = async (el) => {
|
||||
el.classList.remove("invalid");
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(50);
|
||||
el.classList.add("invalid"); // Custom Anim
|
||||
el.classList.add("is-invalid"); // Bootstrap
|
||||
};
|
||||
|
||||
const inputEl = document.getElementById("acp-auth-user-input");
|
||||
const userPasswdInput = inputEl.value;
|
||||
|
||||
if (!userPasswdInput || userPasswdInput.length < 8) {
|
||||
showFailedAnimation(inputEl);
|
||||
return false;
|
||||
}
|
||||
|
||||
const crypto = require("crypto");
|
||||
const encPasswd = crypto
|
||||
.createHash("sha512")
|
||||
.update(userPasswdInput + "EndlessX")
|
||||
.digest("hex")
|
||||
.toUpperCase();
|
||||
|
||||
if (
|
||||
encPasswd ===
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordWithSalt
|
||||
) {
|
||||
const acsDialogAreaEl = document.getElementsByClassName(
|
||||
"aura-config-page-auth-dialog-area"
|
||||
)[0];
|
||||
acsDialogAreaEl.classList.add("acp-ada-hidden");
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
|
||||
acsDialogAreaEl.style = "display: none;";
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(250);
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.showSecondPhaseAnim();
|
||||
return true;
|
||||
} else {
|
||||
showFailedAnimation(inputEl);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
(() => {
|
||||
@@ -167,23 +209,51 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
|
||||
});
|
||||
};
|
||||
|
||||
const showAnimation = async () => {
|
||||
const defaultHeader = document.getElementsByClassName(
|
||||
"index__header__16DmR2a5"
|
||||
)[0];
|
||||
global.__HUGO_AURA_UI_FUNCTIONS__.config.showSecondPhaseAnim = () => {
|
||||
showOperationsAnimation();
|
||||
};
|
||||
|
||||
const handleSettingsAuth = async () => {
|
||||
const isAuthEnabled =
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordEnabled;
|
||||
|
||||
if (!isAuthEnabled) {
|
||||
showOperationsAnimation();
|
||||
} else {
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(50);
|
||||
const acsDialogAreaEl = document.getElementsByClassName(
|
||||
"aura-config-page-auth-dialog-area"
|
||||
)[0];
|
||||
acsDialogAreaEl.style = "";
|
||||
if (
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.appearance
|
||||
.enablePasswdDialogBlur
|
||||
) {
|
||||
acsDialogAreaEl.classList.add("blur-enabled");
|
||||
}
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
|
||||
acsDialogAreaEl.classList.remove("acp-ada-hidden");
|
||||
}
|
||||
};
|
||||
|
||||
const showAnimation = async () => {
|
||||
const auraConfigPageRoot = document.getElementsByClassName(
|
||||
"aura-config-page-root"
|
||||
)[0];
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(200);
|
||||
auraConfigPageRoot.className = "aura-config-page-root";
|
||||
|
||||
const defaultHeader = document.getElementsByClassName(
|
||||
"index__header__16DmR2a5"
|
||||
)[0];
|
||||
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
|
||||
defaultHeader.style = "display: none;";
|
||||
showVersionContainerAnimation();
|
||||
showHeaderAnimation();
|
||||
await window.__HUGO_AURA_GLOBAL__.utils.sleep(500);
|
||||
showOperationsAnimation();
|
||||
|
||||
await handleSettingsAuth();
|
||||
};
|
||||
|
||||
const onMounted = () => {
|
||||
|
||||
@@ -6,6 +6,20 @@
|
||||
<li class="nav-item" role="presentation">
|
||||
<button
|
||||
class="nav-link active"
|
||||
id="aura-subpage-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#aura-subpage"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="aura-subpage"
|
||||
aria-selected="true"
|
||||
>
|
||||
Aura 设置
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button
|
||||
class="nav-link"
|
||||
id="auth-subpage-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#auth-subpage"
|
||||
@@ -14,7 +28,7 @@
|
||||
aria-controls="auth-subpage"
|
||||
aria-selected="true"
|
||||
>
|
||||
认证与基础设施
|
||||
认证与环境
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
@@ -35,6 +49,12 @@
|
||||
<div class="tab-content">
|
||||
<div
|
||||
class="tab-pane fade show active"
|
||||
id="aura-subpage"
|
||||
role="tabpanel"
|
||||
aria-labelledby="aura-subpage-tab"
|
||||
></div>
|
||||
<div
|
||||
class="tab-pane fade show"
|
||||
id="auth-subpage"
|
||||
role="tabpanel"
|
||||
aria-labelledby="auth-subpage-tab"
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
const {
|
||||
settingsRenderer,
|
||||
} = require("../../aura/ui/composables/settingsRenderer");
|
||||
const { auraSettings } = require(`${pathBase}/aura`);
|
||||
const { authSettings } = require(`${pathBase}/auth`);
|
||||
const { banAuditSettings } = require(`${pathBase}/audit`);
|
||||
|
||||
const initAuraSubPage = () => {
|
||||
const auraSettingsSubPageEl = document.getElementById("aura-subpage");
|
||||
settingsRenderer(auraSettingsSubPageEl, auraSettings);
|
||||
};
|
||||
|
||||
const initAuthSubPage = () => {
|
||||
const authSubPageEl = document.getElementById("auth-subpage");
|
||||
settingsRenderer(authSubPageEl, authSettings);
|
||||
@@ -19,6 +25,7 @@
|
||||
};
|
||||
|
||||
const onMounted = () => {
|
||||
initAuraSubPage();
|
||||
initAuthSubPage();
|
||||
initBanAuditSubPage();
|
||||
|
||||
|
||||
113
src/aura/ui/pages/configSubPages/disableLimitations/settings/aura.js
Executable file
113
src/aura/ui/pages/configSubPages/disableLimitations/settings/aura.js
Executable file
@@ -0,0 +1,113 @@
|
||||
const auraSettings = [
|
||||
{
|
||||
id: 0,
|
||||
categoryName: "安全性",
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
id: "enableAuraSettingsPasswd",
|
||||
type: "switch",
|
||||
name: "启用访问密码",
|
||||
description: "启用后, Aura 设置 UI 需要输入密码才可访问",
|
||||
restart: false,
|
||||
reload: false,
|
||||
tip: true,
|
||||
tipTitle: "在 0.1.1-beta 版本发布后, 启用访问密码将加密配置文件",
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: false,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings
|
||||
.settingsPasswordEnabled;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordEnabled =
|
||||
newVal;
|
||||
// TODO: Trigger enc config
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
id: "auraSettingsPasswd",
|
||||
type: "input",
|
||||
subType: "password",
|
||||
name: "访问密码",
|
||||
description: "此密码将用于访问 Aura 设置 UI",
|
||||
restart: false,
|
||||
reload: false,
|
||||
associateVal: ["auraSettings.settingsPasswordEnabled"],
|
||||
auraIf: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings
|
||||
.settingsPasswordEnabled;
|
||||
},
|
||||
defaultValue: "",
|
||||
placeHolder: "留空表示不修改, 保留已设置值",
|
||||
valueGetter: () => {
|
||||
return "";
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (newVal === "" || !newVal) return { valid: true };
|
||||
if (newVal.length < 8)
|
||||
return { valid: false, hint: "请输入至少 8 位密码" };
|
||||
|
||||
const hasNumber = /[0-9]/.test(newVal);
|
||||
const hasLetter = /[a-zA-Z]/.test(newVal);
|
||||
const hasSpecial = /[^a-zA-Z0-9]/.test(newVal);
|
||||
|
||||
const typeCount = [hasNumber, hasLetter, hasSpecial].filter(
|
||||
Boolean
|
||||
).length;
|
||||
|
||||
if (typeCount < 2) {
|
||||
return {
|
||||
valid: false,
|
||||
hint: "请包含数字 / 字母 / 特殊字符中的至少 2 种",
|
||||
};
|
||||
}
|
||||
|
||||
const crypto = require("crypto");
|
||||
const result = crypto
|
||||
.createHash("sha512")
|
||||
.update(newVal + "EndlessX")
|
||||
.digest("hex")
|
||||
.toUpperCase();
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.settingsPasswordWithSalt =
|
||||
result;
|
||||
return { valid: true };
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
categoryName: "外观",
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
id: "enablePasswdDialogBlur",
|
||||
type: "switch",
|
||||
name: "密码验证框毛玻璃效果",
|
||||
description: "启用后, 密码验证时, 背景将具有毛玻璃效果",
|
||||
restart: false,
|
||||
reload: false,
|
||||
tip: true,
|
||||
tipTitle: "不建议在较旧 (如 i5 8 代) 机型上开启, 可能导致性能问题",
|
||||
associateVal: null,
|
||||
auraIf: () => true,
|
||||
defaultValue: true,
|
||||
valueGetter: () => {
|
||||
return global.__HUGO_AURA_CONFIG__.auraSettings.appearance
|
||||
.enablePasswdDialogBlur;
|
||||
},
|
||||
callbackFn: (newVal) => {
|
||||
if (typeof newVal !== "boolean") return;
|
||||
global.__HUGO_AURA_CONFIG__.auraSettings.appearance.enablePasswdDialogBlur =
|
||||
newVal;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = { auraSettings };
|
||||
@@ -1,7 +1,7 @@
|
||||
const authSettings = [
|
||||
{
|
||||
id: 0,
|
||||
categoryName: "身份验证",
|
||||
categoryName: "管家身份验证",
|
||||
child: [
|
||||
{
|
||||
index: 0,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const __AURA_VERSION__ = "0.1.0-beta";
|
||||
const __AURA_VERSION__ = "0.1.1-pre-I";
|
||||
|
||||
(() => {
|
||||
if (!global.__HUGO_AURA__) {
|
||||
|
||||
Reference in New Issue
Block a user