feat: reveal encrypted config secrets on demand
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -276,6 +276,10 @@ export const api = {
|
||||
body: "{}",
|
||||
}),
|
||||
configs: () => request<SystemConfigItem[]>("/admin/config"),
|
||||
revealConfigSecret: (configKey: string) =>
|
||||
request<{ configKey: string; configValue: string }>(
|
||||
`/admin/config/${encodeURIComponent(configKey)}/secret`,
|
||||
),
|
||||
saveConfig: (payload: Record<string, unknown>) =>
|
||||
request<SystemConfigItem>("/admin/config", { method: "PUT", body: JSON.stringify(payload) }),
|
||||
ssoConfig: () => request<{ userClientUrl: string }>("/admin/sso/config"),
|
||||
|
||||
Reference in New Issue
Block a user