diff --git a/app/(dashboard)/sse/page.tsx b/app/(dashboard)/sse/page.tsx
index e523dd5..c6c65f1 100644
--- a/app/(dashboard)/sse/page.tsx
+++ b/app/(dashboard)/sse/page.tsx
@@ -49,7 +49,7 @@ const KEY_LIST_LIMIT = 20
const DEFAULT_PENDING_DELETE_DAYS = 7
type ConfigFormState = {
- backendType: "local" | "vault"
+ backendType: "local" | "vault-kv2" | "vault-transit"
keyDir: string
filePermissions: string
defaultKeyId: string
@@ -134,25 +134,36 @@ function isAbsolutePath(value: string) {
return /^(?:[A-Za-z]:[\\/]|\\\\|\/)/.test(value.trim())
}
+function normalizeBackendType(value?: string | null): ConfigFormState["backendType"] {
+ switch (value) {
+ case "Vault":
+ case "VaultKV2":
+ return "vault-kv2"
+ case "VaultTransit":
+ return "vault-transit"
+ default:
+ return "local"
+ }
+}
+
function buildFormStateFromStatus(status: KmsServiceStatusResponse | null): ConfigFormState {
if (!status) return INITIAL_FORM_STATE
const summary = status.config_summary
const backendSummary = summary?.backend_summary
const cacheSummary = summary?.cache_summary
+ const backendType = normalizeBackendType(status.backend_type ?? summary?.backend_type)
return {
- backendType: status.backend_type === "Vault" ? "vault" : "local",
+ backendType,
keyDir: backendSummary?.key_dir ?? "",
filePermissions: String(backendSummary?.file_permissions ?? 384),
defaultKeyId: summary?.default_key_id ?? "",
- timeoutSeconds: String(backendSummary?.timeout_seconds ?? 30),
- retryAttempts: String(backendSummary?.retry_attempts ?? 3),
+ timeoutSeconds: String(summary?.timeout_seconds ?? 30),
+ retryAttempts: String(summary?.retry_attempts ?? 3),
enableCache: summary?.enable_cache ?? cacheSummary?.enabled ?? true,
- maxCachedKeys: String(summary?.max_cached_keys ?? cacheSummary?.max_cached_keys ?? 1000),
- cacheTtlSeconds: String(
- summary?.cache_ttl_seconds ?? cacheSummary?.cache_ttl_seconds ?? cacheSummary?.ttl_seconds ?? 3600,
- ),
+ maxCachedKeys: String(summary?.max_cached_keys ?? cacheSummary?.max_keys ?? 1000),
+ cacheTtlSeconds: String(summary?.cache_ttl_seconds ?? cacheSummary?.ttl_seconds ?? 3600),
address: backendSummary?.address ?? "",
vaultToken: "",
namespace: backendSummary?.namespace ?? "",
@@ -212,6 +223,7 @@ export default function SSEPage() {
const statusKind = React.useMemo(() => getStatusKind(status), [status])
const isRunning = statusKind === "Running"
const hasConfiguration = statusKind !== "NotConfigured"
+ const hasStoredVaultCredentials = status?.config_summary?.backend_summary?.has_stored_credentials === true
const statusBadgeValue =
statusKind === "Error" ? "Error" : typeof status?.status === "string" ? status.status : statusKind
@@ -224,7 +236,7 @@ export default function SSEPage() {
if (syncForm) {
setFormState((current) => {
const next = buildFormStateFromStatus(res)
- if (current.vaultToken && next.backendType === "vault") {
+ if (current.vaultToken && next.backendType !== "local") {
next.vaultToken = current.vaultToken
}
return next
@@ -339,7 +351,7 @@ export default function SSEPage() {
return {
payload: {
- backend_type: "local",
+ backend_type: "Local",
key_dir: formState.keyDir.trim(),
file_permissions: parseOptionalInteger(formState.filePermissions) ?? 384,
default_key_id: defaultKeyId || undefined,
@@ -355,7 +367,7 @@ export default function SSEPage() {
if (!formState.address.trim()) {
return { error: t("Please enter Vault server address") }
}
- if (!formState.vaultToken.trim()) {
+ if (!formState.vaultToken.trim() && !hasStoredVaultCredentials) {
return { error: t("Please enter Vault token") }
}
if (!formState.mountPath.trim()) {
@@ -364,7 +376,7 @@ export default function SSEPage() {
return {
payload: {
- backend_type: "vault",
+ backend_type: formState.backendType === "vault-kv2" ? "VaultKV2" : "VaultTransit",
address: formState.address.trim(),
auth_method: {
Token: {
@@ -373,8 +385,12 @@ export default function SSEPage() {
},
namespace: formState.namespace.trim() || null,
mount_path: formState.mountPath.trim(),
- kv_mount: formState.kvMount.trim() || null,
- key_path_prefix: formState.keyPathPrefix.trim() || null,
+ ...(formState.backendType === "vault-kv2"
+ ? {
+ kv_mount: formState.kvMount.trim() || null,
+ key_path_prefix: formState.keyPathPrefix.trim() || null,
+ }
+ : {}),
skip_tls_verify: formState.skipTlsVerify,
default_key_id: defaultKeyId || undefined,
timeout_seconds: timeoutSeconds ?? 30,
@@ -385,7 +401,7 @@ export default function SSEPage() {
},
}
},
- [formState, t],
+ [formState, hasStoredVaultCredentials, t],
)
const submitConfiguration = React.useCallback(
@@ -736,7 +752,8 @@ export default function SSEPage() {
{t("Local filesystem")}
- {t("HashiCorp Vault Transit Engine")}
+ {t("HashiCorp Vault KV2")}
+ {t("HashiCorp Vault Transit Engine")}
@@ -814,11 +831,19 @@ export default function SSEPage() {
type="password"
value={formState.vaultToken}
onChange={(event) => updateFormState("vaultToken", event.target.value)}
- placeholder={t("Enter your Vault authentication token")}
+ placeholder={
+ hasStoredVaultCredentials
+ ? t("Stored token is hidden. Enter a new token only to replace it.")
+ : t("Enter your Vault authentication token")
+ }
autoComplete="off"
/>
- {t("Required: Vault authentication token")}
+
+ {hasStoredVaultCredentials
+ ? t("Leave blank to keep the stored Vault token.")
+ : t("Required: Vault authentication token")}
+
@@ -845,27 +870,31 @@ export default function SSEPage() {
-
- {t("KV Mount")}
-
- updateFormState("kvMount", event.target.value)}
- placeholder="secret"
- />
-
-
+ {formState.backendType === "vault-kv2" && (
+ <>
+
+ {t("KV Mount")}
+
+ updateFormState("kvMount", event.target.value)}
+ placeholder="secret"
+ />
+
+
-
- {t("Key Path Prefix")}
-
- updateFormState("keyPathPrefix", event.target.value)}
- placeholder="rustfs/kms/keys"
- />
-
-
+
+ {t("Key Path Prefix")}
+
+ updateFormState("keyPathPrefix", event.target.value)}
+ placeholder="rustfs/kms/keys"
+ />
+
+
+ >
+ )}
diff --git a/components/buckets/info.tsx b/components/buckets/info.tsx
index 193149e..b2ca1ce 100644
--- a/components/buckets/info.tsx
+++ b/components/buckets/info.tsx
@@ -275,7 +275,7 @@ export function BucketInfo({ bucketName }: BucketInfoProps) {
(keys as { keys?: Array<{ key_id?: string; tags?: { name?: string }; description?: string }> })?.keys ?? []
setKmsKeyOptions(
list.map((k) => ({
- label: k.tags?.name ?? k.description ?? `Key-${(k.key_id ?? "").slice(0, 8)}`,
+ label: k.tags?.name ?? k.description ?? `Key-${(k.key_id ?? "").slice(0, 24)}`,
value: k.key_id ?? "",
})),
)
diff --git a/i18n/locales/en-US.json b/i18n/locales/en-US.json
index 18c8165..06e0fcc 100644
--- a/i18n/locales/en-US.json
+++ b/i18n/locales/en-US.json
@@ -249,6 +249,7 @@
"Enter AppRole Role ID": "Enter AppRole Role ID",
"Enter AppRole Secret ID": "Enter AppRole Secret ID",
"Enter your Vault authentication token": "Enter your Vault authentication token",
+ "Leave blank to keep the stored Vault token.": "Leave blank to keep the stored Vault token.",
"Enterprise": "Enterprise",
"Enterprise License": "Enterprise License",
"Enterprise Service Level": "Enterprise Service Level",
@@ -314,6 +315,7 @@
"Get Help": "Get Help",
"Groups": "Groups",
"HashiCorp Encryption": "HashiCorp Encryption",
+ "HashiCorp Vault KV2": "HashiCorp Vault KV2",
"HashiCorp Vault Transit Engine": "HashiCorp Vault Transit Engine",
"Health Check Interval (seconds)": "Health Check Interval (seconds)",
"High Memory Usage Warning": "High Memory Usage Warning",
@@ -619,6 +621,7 @@
"Replication": "Replication",
"Request timeout in seconds, default: 30": "Request timeout in seconds, default: 30",
"Required: Vault authentication token": "Required: Vault authentication token",
+ "Stored token is hidden. Enter a new token only to replace it.": "Stored token is hidden. Enter a new token only to replace it.",
"Reset": "Reset",
"Reset to Default": "Reset to Default",
"Reset to default successfully": "Reset to default successfully",
diff --git a/i18n/locales/zh-CN.json b/i18n/locales/zh-CN.json
index 435682f..143c8ce 100644
--- a/i18n/locales/zh-CN.json
+++ b/i18n/locales/zh-CN.json
@@ -252,6 +252,7 @@
"Enter AppRole Role ID": "输入 AppRole 角色 ID",
"Enter AppRole Secret ID": "输入AppRole Secret ID",
"Enter your Vault authentication token": "输入您的Vault认证令牌",
+ "Leave blank to keep the stored Vault token.": "留空则继续使用已存储的 Vault 令牌。",
"Enterprise": "企业版",
"Enterprise License": "企业版许可证",
"Enterprise Service Level": "企业服务级别",
@@ -321,6 +322,7 @@
"Get Notification Config Failed": "获取通知配置失败",
"Groups": "分组",
"HashiCorp Encryption": "HashiCorp 加密",
+ "HashiCorp Vault KV2": "HashiCorp Vault KV2",
"HashiCorp Vault Transit Engine": "HashiCorp Vault Transit 引擎",
"Health Check Interval (seconds)": "健康检查间隔(秒)",
"High Memory Usage Warning": "高内存使用警告",
@@ -637,6 +639,7 @@
"Replicate Delete": "复制删除",
"Request timeout in seconds, default: 30": "请求超时时间(秒),默认:30",
"Required: Vault authentication token": "必需:Vault 认证令牌",
+ "Stored token is hidden. Enter a new token only to replace it.": "已存储的令牌不会显示;仅在需要替换时输入新令牌。",
"Reset": "重置",
"Reset to Default": "重置为默认",
"Reset to default successfully": "重置为默认成功",
diff --git a/types/kms.ts b/types/kms.ts
index c6a8e2e..31b0519 100644
--- a/types/kms.ts
+++ b/types/kms.ts
@@ -1,9 +1,10 @@
export type KmsServiceStatus = "NotConfigured" | "Configured" | "Running" | { Error: string }
-export type KmsBackendType = "Local" | "Vault"
+export type KmsBackendType = "Local" | "Vault" | "VaultKV2" | "VaultTransit"
export interface KmsCacheSummary {
enabled?: boolean
+ max_keys?: number | null
max_cached_keys?: number | null
cache_ttl_seconds?: number | null
ttl_seconds?: number | null
@@ -12,18 +13,22 @@ export interface KmsCacheSummary {
export interface KmsBackendSummary {
key_dir?: string | null
file_permissions?: number | null
+ has_master_key?: boolean | null
address?: string | null
+ auth_method_type?: string | null
+ has_stored_credentials?: boolean | null
namespace?: string | null
mount_path?: string | null
kv_mount?: string | null
key_path_prefix?: string | null
skip_tls_verify?: boolean | null
- timeout_seconds?: number | null
- retry_attempts?: number | null
}
export interface KmsConfigSummary {
+ backend_type?: KmsBackendType | null
default_key_id?: string | null
+ timeout_seconds?: number | null
+ retry_attempts?: number | null
enable_cache?: boolean | null
max_cached_keys?: number | null
cache_ttl_seconds?: number | null
@@ -49,7 +54,7 @@ export interface KmsStartRequest {
}
export interface KmsLocalConfigPayload {
- backend_type: "local"
+ backend_type: "Local"
key_dir: string
file_permissions?: number
default_key_id?: string
@@ -67,7 +72,7 @@ export interface KmsVaultTokenAuthMethod {
}
export interface KmsVaultConfigPayload {
- backend_type: "vault"
+ backend_type: "Vault"
address: string
auth_method: KmsVaultTokenAuthMethod
namespace?: string | null
@@ -83,7 +88,43 @@ export interface KmsVaultConfigPayload {
cache_ttl_seconds?: number
}
-export type KmsConfigPayload = KmsLocalConfigPayload | KmsVaultConfigPayload
+export interface KmsVaultKV2ConfigPayload {
+ backend_type: "VaultKV2"
+ address: string
+ auth_method: KmsVaultTokenAuthMethod
+ namespace?: string | null
+ mount_path: string
+ kv_mount?: string | null
+ key_path_prefix?: string | null
+ skip_tls_verify?: boolean
+ default_key_id?: string
+ timeout_seconds?: number
+ retry_attempts?: number
+ enable_cache?: boolean
+ max_cached_keys?: number
+ cache_ttl_seconds?: number
+}
+
+export interface KmsVaultTransitConfigPayload {
+ backend_type: "VaultTransit"
+ address: string
+ auth_method: KmsVaultTokenAuthMethod
+ namespace?: string | null
+ mount_path: string
+ skip_tls_verify?: boolean
+ default_key_id?: string
+ timeout_seconds?: number
+ retry_attempts?: number
+ enable_cache?: boolean
+ max_cached_keys?: number
+ cache_ttl_seconds?: number
+}
+
+export type KmsConfigPayload =
+ | KmsLocalConfigPayload
+ | KmsVaultConfigPayload
+ | KmsVaultKV2ConfigPayload
+ | KmsVaultTransitConfigPayload
export interface KmsKeyInfo {
key_id: string