feat: reveal encrypted config secrets on demand

This commit is contained in:
2026-08-11 18:20:22 +08:00
parent 71541f9a22
commit ff4ba1148b
3 changed files with 95 additions and 6 deletions

View File

@@ -1,6 +1,9 @@
<script setup lang="ts">
import { computed, ref } from "vue";
import { Hide, View } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import { computed, reactive, ref } from "vue";
import { api } from "../services/api";
import {
systemSettingSections,
type SystemSettingDefinition,
@@ -127,6 +130,8 @@ const wideSettingKeys = new Set([
]);
const activeSectionTitle = ref(systemSettingSections[0]?.title || "");
const revealedSecrets = reactive<Record<string, string>>({});
const revealingSecrets = reactive<Record<string, boolean>>({});
const activeSection = computed(
() =>
@@ -200,6 +205,33 @@ function updateSetting(key: string, value: unknown) {
if (value === undefined || value === null) return;
emit("update", key, value as SystemSettingValue);
}
async function toggleSecret(setting: SystemSettingDefinition) {
if (revealedSecrets[setting.key] !== undefined) {
delete revealedSecrets[setting.key];
return;
}
const currentValue = String(props.values[setting.key] ?? "");
if (currentValue && currentValue !== "******") {
revealedSecrets[setting.key] = currentValue;
return;
}
if (revealingSecrets[setting.key]) return;
revealingSecrets[setting.key] = true;
try {
const result = await api.revealConfigSecret(setting.key);
revealedSecrets[setting.key] = result.configValue;
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : "敏感配置读取失败");
} finally {
revealingSecrets[setting.key] = false;
}
}
function updatePasswordSetting(key: string, value: string) {
if (revealedSecrets[key] !== undefined) revealedSecrets[key] = value;
updateSetting(key, value);
}
</script>
<template>
@@ -326,12 +358,26 @@ function updateSetting(key: string, value: unknown) {
<el-input
v-else-if="setting.type === 'password'"
:id="`setting-${setting.key}`"
:model-value="String(values[setting.key] ?? '')"
:model-value="revealedSecrets[setting.key] ?? String(values[setting.key] ?? '')"
:placeholder="setting.placeholder"
type="password"
show-password
@update:model-value="updateSetting(setting.key, $event)"
/>
:type="revealedSecrets[setting.key] !== undefined ? 'text' : 'password'"
@update:model-value="updatePasswordSetting(setting.key, $event)"
>
<template #suffix>
<button
type="button"
class="secret-visibility-toggle"
:class="{ loading: revealingSecrets[setting.key] }"
:aria-label="revealedSecrets[setting.key] !== undefined ? '隐藏真实内容' : '查看真实内容'"
@click="toggleSecret(setting)"
>
<el-icon>
<Hide v-if="revealedSecrets[setting.key] !== undefined" />
<View v-else />
</el-icon>
</button>
</template>
</el-input>
<el-select
v-else-if="setting.type === 'select'"
:id="`setting-${setting.key}`"
@@ -721,6 +767,20 @@ function updateSetting(key: string, value: unknown) {
width: 100%;
}
.secret-visibility-toggle {
display: inline-flex;
padding: 2px;
border: 0;
background: transparent;
cursor: pointer;
color: #66736f;
}
.secret-visibility-toggle.loading {
pointer-events: none;
opacity: 0.45;
}
.config-field :deep(.el-input__wrapper),
.config-field :deep(.el-select__wrapper),
.config-field :deep(.el-input-number) {