diff --git a/app/(dashboard)/403/page.tsx b/app/(dashboard)/403/page.tsx
index 76983d97..9af73188 100644
--- a/app/(dashboard)/403/page.tsx
+++ b/app/(dashboard)/403/page.tsx
@@ -2,7 +2,7 @@
import { useRouter } from "next/navigation"
import { useTranslation } from "react-i18next"
-import { Empty, EmptyContent, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from "@/components/ui/empty"
+import { RiArrowLeftLine } from "@remixicon/react"
import { Button } from "@/components/ui/button"
import { useAuth } from "@/contexts/auth-context"
import { useFirstAccessibleDashboardRoute } from "@/hooks/use-first-accessible-dashboard-route"
@@ -26,36 +26,22 @@ export default function ForbiddenPage() {
}
return (
-
-
-
-
-
-
-
- {t("Access Denied")}
-
- {t(
- "You do not have permission to access this page. This may be due to insufficient permissions or not being logged in.",
- )}
-
-
-
-
-
-
+
+
+ 403
+
+
+
{t("Access Denied")}
+
+ {t(
+ "You do not have permission to access this page. This may be due to insufficient permissions or not being logged in.",
+ )}
+
{/* Peer sections separated by one divider system, no nested frames. */}
-
+
{t("Password")}
@@ -114,22 +117,27 @@ export default function AccountSecurityPage() {
: t("This identity's password is managed outside the console.")}
- {canManagePassword ? (
- setPasswordOpen(true)}>{t("Change Password")}
- ) : (
-
-
- {info.credentials_source === "env"
- ? t(
- "This identity is provisioned from the server environment (RUSTFS_ACCESS_KEY). Change it by restarting the server with new values.",
- )
- : t("This identity's password is managed outside the console.")}
-
-
- )}
+
+ {canManagePassword ? (
+ setPasswordOpen(true)}>{t("Change Password")}
+ ) : (
+
+
+ {info.credentials_source === "env"
+ ? t(
+ "This identity is provisioned from the server environment (RUSTFS_ACCESS_KEY). Change it by restarting the server with new values.",
+ )
+ : t("This identity's password is managed outside the console.")}
+
+
+ )}
+
-
+
@@ -139,74 +147,75 @@ export default function AccountSecurityPage() {
{mfa?.enabled ? t("On") : t("Off")}
+
+ {mfa?.enabled ? (
+ <>
+
+ {mfa.activated_at && (
+ <>
+
{t("Enabled on")}
+
{formatDateTime(mfa.activated_at)}
+ >
+ )}
+ {mfa.last_verified_at && (
+ <>
+
{t("Last used")}
+
{formatDateTime(mfa.last_verified_at)}
+ >
+ )}
+
{t("Recovery codes remaining")}
+
{mfa.recovery_codes_remaining}
+
- {mfa?.enabled ? (
- <>
-
- {mfa.activated_at && (
- <>
-
{t("Enabled on")}
-
{formatDateTime(mfa.activated_at)}
- >
+ {mfa.recovery_codes_remaining === 0 && (
+
+
+ {t(
+ "You have no recovery codes left. Generate a new set so you can get back in if you lose your authenticator.",
+ )}
+
+
)}
- {mfa.last_verified_at && (
- <>
-
{t("Last used")}
-
{formatDateTime(mfa.last_verified_at)}
- >
+ {recoveryCodesRunningLow(mfa.recovery_codes_remaining) && (
+
+
+ {t("You are running low on recovery codes. Generate a new set to be safe.")}
+
+
)}
-
{t("Recovery codes remaining")}
-
{mfa.recovery_codes_remaining}
-
-
- {mfa.recovery_codes_remaining === 0 && (
-
-
- {t(
- "You have no recovery codes left. Generate a new set so you can get back in if you lose your authenticator.",
- )}
-
-
- )}
- {recoveryCodesRunningLow(mfa.recovery_codes_remaining) && (
-
-
- {t("You are running low on recovery codes. Generate a new set to be safe.")}
-
-
- )}
-
-
-
- {t("Current Site")}
-
- {t("The server detects the local site identity from the active RustFS deployment.")}
-
-
-
-
-
- {localPeer?.endpoint || localMetric?.endpoint || "--"}
-
- }
- />
- {localDeploymentId || "--"}}
- />
- {info?.enabled && !localDeploymentId ? (
-
- {localSite.ambiguousName
- ? t("Multiple peers share the local site name, so the current site cannot be identified.")
- : t("The current site could not be identified because the server did not report its deployment ID.")}
-
+ {showSetup ? (
+
+
+ {t("Not configured")}
+
+ {t("Add a remote site to initialize site replication for this deployment.")}
+
+
+ {t(
+ "The current site is detected automatically by the server. Add one or more remote sites to join the replication group.",
+ )}
+
+ {canManageSites ? (
+ setNewFormOpen(true)}>
+
+ {t("Add Site")}
+
) : null}
-
-
-
-
-
- {t("Replication Summary")}
-
- {t("Overview of replicated configuration and object replication activity.")}
-
-
-
- {t("Enabled")} : {t("Not configured")}
- }
- />
-
-
-
-
-
-
-
-
- {t("Replicated Scope")}
-
- {statusUnavailable
- ? t("This server does not provide the detailed site replication status endpoint yet.")
- : t(
- "Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.",
+
+ {localSite.ambiguousName
+ ? t("Multiple peers share the local site name, so the current site cannot be identified.")
+ : t(
+ "The current site could not be identified because the server did not report its deployment ID.",
+ )}
+
+ ) : null}
+
+
+
+
+
{t("Replication Summary")}
+
+ {t("Overview of replicated configuration and object replication activity.")}
+
+ {statusUnavailable
+ ? t("This server does not provide the detailed site replication status endpoint yet.")
+ : t(
+ "Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.",
+ )}
+
- ) : null}
-
-
-
-
-
-
- {t("Peer Sites")}
-
- {t(
- "Peer membership and editable replication controls, aligned with the RustFS backend site replication API and MinIO-style site management flow.",
- )}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
{t("Replication Controls")}
+
+ {t(
+ "Service account and global lifecycle expiry replication settings for the site replication group.",
+ )}
+
+ {info?.serviceAccountAccessKey ? : null}
+
+
+ {info.serviceAccountAccessKey}
+ ) : (
+ t("Created automatically after the first remote site is added")
+ )
+ }
+ />
+ {t("Enabled")} : {t("Disabled")}
+ }
+ />
+ {canManageSites ? (
+
+ {t("Configure the KMS backend, cache policy, and default SSE key.")}
+
+
+ {configFormError ? (
+
+ {t("Please fix the form errors before saving")}
+ {configFormError}
+
+ ) : null}
+ {configBaselineConflict ? (
+
+ {t("KMS configuration changed")}
+ {configBaselineConflict}
+
+ ) : null}
+ {formState.backendType === "local" ? (
+
+ {t("Warning")}
+
+ {localKmsConfigured
+ ? t(
+ "Local KMS is intended for evaluation only. The key directory and file permissions are locked after configuration; changing or rotating the local master key is not supported.",
+ )
+ : t(
+ "Local KMS is intended for evaluation only. Keys created without a server-managed master key are stored using plaintext development mode. Do not use this backend for production data.",
+ )}
+
+
+ ) : null}
+ {unsupportedKmsReadOnly ? (
+
+ {t("Unsupported KMS backend")}
+
+ {t(
+ "This KMS backend is not supported by this Console version. Upgrade Console before reconfiguring KMS.",
+ )}
+
+
+ ) : null}
+