Files
aiops/frontend/src/components/system/system-settings/SecuritySettingsTab.tsx
T
attacker c6e0d6ca57 refactor(i18n): U42 残余硬编码中文彻底清零 — 33文件54处→0, zh/en 7847→7898对齐
全量 JSX children + toast/confirm + sr-only 描述硬编码中文清零:
- Kubernetes模块: UpdateStrategyCard maxSurge/maxUnavailable说明 + IngressCreateForm host域名/Service端口 + ServiceCreateForm 端口配置/selector + DeploymentEditService 无selector/个关联/暂无Service/Ingress + ClusterOverviewPage 核 + KubernetesClusterDialog 已配置 + KubectlTerminal/ResourceVisualEditor/CreateHPADialog/DeploymentEditContainers/Storage + PodInstancesTable/UpdateStrategySelectorCards
- AI模块: PredictiveCapacityTab AI深度解读/紧急/警告/7天预测/预计N天 + PredictiveCostTab AI深度解读 + event-handler.ts 高危操作确认toast
- Deploy模块: DeployCredentials 启用/禁用 + DeployRBAC 删除角色confirm + WorkspaceTermDialog + CredFormDialog Windows用户
- Monitor模块: AlertRulesTab 删除confirm/分钟 + PrometheusDatasourceManager/LogDatasourcePage 删除confirm + QueryHistoryPanel 条
- System模块: ServerFileBrowser/TerminalDialog/RdpAuditViewer sr-only + SystemManager/CredentialManager/TerminalSessionsTab 删除confirm + SecuritySettingsTab 30分钟/2小时
- Task模块: CronJobViewDialog 次

新增 61 翻译键 (k8s.editAdvanced 6 + k8s.editService 11 + k8s.cluster 2 + k8s.misc 9 + aiops.capacity 9 + aiops.cost 1 + aiops.cli 2 + deploy 4 + monitor 5 + system 10 + task 1 + common 2)
tsc -b=0, npm build✓ (17.71s), ESLint=0/43/any=0, go build=0, 17/17 测试通过
2026-07-14 00:51:36 +08:00

310 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Lock, Key, Loader2 } from "lucide-react";
import { useTranslation } from "react-i18next";
import { useUserStore } from "@/store";
import type { SystemSettingsTabProps } from "../SystemSettingsTab";
// 安全设置 Tab:安全策略 + 会话设置(水印/录制/剪贴板/上传/下载/命令策略/审批/超时/SSO) // Logan.Li
type SecuritySettingsTabProps = Pick<
SystemSettingsTabProps,
| "sessionWatermarkEnabled"
| "setSessionWatermarkEnabled"
| "sessionRecordEnabled"
| "setSessionRecordEnabled"
| "clipboardEnabled"
| "setClipboardEnabled"
| "fileUploadEnabled"
| "setFileUploadEnabled"
| "fileDownloadEnabled"
| "setFileDownloadEnabled"
| "commandPolicyEnabled"
| "setCommandPolicyEnabled"
| "approvalRequired"
| "setApprovalRequired"
| "auditLoading"
| "auditApplying"
| "handleAuditApply"
>;
export function SecuritySettingsTab(props: SecuritySettingsTabProps) {
const { t } = useTranslation();
const currentUsername = useUserStore(
(state) => state.user?.username || "admin",
);
const {
sessionWatermarkEnabled,
setSessionWatermarkEnabled,
sessionRecordEnabled,
setSessionRecordEnabled,
clipboardEnabled,
setClipboardEnabled,
fileUploadEnabled,
setFileUploadEnabled,
fileDownloadEnabled,
setFileDownloadEnabled,
commandPolicyEnabled,
setCommandPolicyEnabled,
approvalRequired,
setApprovalRequired,
auditLoading,
auditApplying,
handleAuditApply,
} = props;
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 pt-2">
<Card className="dg-card">
<CardHeader>
<CardTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
<Lock className="h-5 w-5 dg-text-blue" />
{t("system.security")}
</CardTitle>
<CardDescription className="dg-text-muted">
{t("system.securityDesc")}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.forcePasswordComplexity")}
</p>
<p className="text-xs dg-text-muted">
{t("system.forcePasswordComplexityDesc")}
</p>
</div>
<Switch disabled className="data-[state=checked]:bg-blue-600" />
</div>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.loginLockout")}
</p>
<p className="text-xs dg-text-muted">
{t("system.loginLockoutDesc")}
</p>
</div>
<Switch
disabled
defaultChecked
className="data-[state=checked]:bg-blue-600"
/>
</div>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.twoFactor")}
</p>
<p className="text-xs dg-text-muted">
{t("system.twoFactorDesc")}
</p>
</div>
<Switch disabled className="data-[state=checked]:bg-blue-600" />
</div>
</CardContent>
</Card>
<Card className="dg-card">
<CardHeader>
<CardTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
<Key className="h-5 w-5 dg-text-yellow" />
{t("system.sessionSettings")}
</CardTitle>
<CardDescription className="dg-text-muted">
{t("system.sessionSettingsDesc")}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between gap-3">
<div className="min-w-0">
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.sessionWatermark")}
</p>
<p className="text-xs dg-text-muted">
{t("system.sessionWatermarkDesc", {
user: currentUsername,
})}
</p>
</div>
<Switch
checked={sessionWatermarkEnabled}
onCheckedChange={setSessionWatermarkEnabled}
className="data-[state=checked]:bg-blue-600"
/>
</div>
<div className="rounded border border-[var(--dg-border)] bg-[#101418] px-3 py-4 overflow-hidden">
<div className="relative h-24 rounded bg-black/70">
<div
className="absolute inset-0 opacity-80"
style={{
backgroundImage: `repeating-linear-gradient(-24deg, transparent 0 42px, rgba(255,255,255,0.12) 43px 44px, transparent 45px 120px)`,
}}
/>
<div className="absolute inset-0 grid grid-cols-2 place-items-center -rotate-[18deg] text-lg font-semibold tracking-normal text-white/30">
<span>{currentUsername}</span>
<span>{currentUsername}</span>
<span>{currentUsername}</span>
<span>{currentUsername}</span>
</div>
</div>
</div>
<div className="grid grid-cols-1 gap-3">
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.sessionRecordEnabled")}
</p>
<p className="text-xs dg-text-muted">
{t("system.sessionRecordEnabledDesc")}
</p>
</div>
<Switch
checked={sessionRecordEnabled}
onCheckedChange={setSessionRecordEnabled}
className="data-[state=checked]:bg-blue-600"
/>
</div>
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.clipboardEnabled")}
</p>
<p className="text-xs dg-text-muted">
{t("system.clipboardEnabledDesc")}
</p>
</div>
<Switch
checked={clipboardEnabled}
onCheckedChange={setClipboardEnabled}
className="data-[state=checked]:bg-blue-600"
/>
</div>
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.fileUploadEnabled")}
</p>
<p className="text-xs dg-text-muted">
{t("system.fileUploadEnabledDesc")}
</p>
</div>
<Switch
checked={fileUploadEnabled}
onCheckedChange={setFileUploadEnabled}
className="data-[state=checked]:bg-blue-600"
/>
</div>
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.fileDownloadEnabled")}
</p>
<p className="text-xs dg-text-muted">
{t("system.fileDownloadEnabledDesc")}
</p>
</div>
<Switch
checked={fileDownloadEnabled}
onCheckedChange={setFileDownloadEnabled}
className="data-[state=checked]:bg-blue-600"
/>
</div>
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.commandPolicyEnabled")}
</p>
<p className="text-xs dg-text-muted">
{t("system.commandPolicyEnabledDesc")}
</p>
</div>
<Switch
checked={commandPolicyEnabled}
onCheckedChange={setCommandPolicyEnabled}
className="data-[state=checked]:bg-blue-600"
/>
</div>
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.approvalRequired")}
</p>
<p className="text-xs dg-text-muted">
{t("system.approvalRequiredDesc")}
</p>
</div>
<Switch
checked={approvalRequired}
onCheckedChange={setApprovalRequired}
className="data-[state=checked]:bg-blue-600"
/>
</div>
</div>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.sessionTimeoutTime")}
</p>
<p className="text-xs dg-text-muted">
{t("system.sessionTimeoutDesc")}
</p>
</div>
<Select defaultValue="30" disabled>
<SelectTrigger className="w-32 h-8 dg-input">
<SelectValue />
</SelectTrigger>
<SelectContent className="dg-input">
<SelectItem value="15">
{t("monitor.traceMgr.min15")}
</SelectItem>
<SelectItem value="30">{t("common.minutes30")}</SelectItem>
<SelectItem value="60">
{t("monitor.traceMgr.hour1")}
</SelectItem>
<SelectItem value="120">{t("common.hours2")}</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--dg-text-primary)]">
{t("system.sso")}
</p>
<p className="text-xs dg-text-muted">
{t("system.singleSignOnDesc")}
</p>
</div>
<Switch disabled className="data-[state=checked]:bg-blue-600" />
</div>
<div className="flex justify-end pt-1">
<Button
size="sm"
className="dg-btn-primary"
onClick={handleAuditApply}
disabled={auditLoading || auditApplying}
>
{auditApplying ? (
<>
<Loader2 className="h-3.5 w-3.5 mr-1.5 animate-spin" />
{t("system.applyingDot")}
</>
) : (
t("common.apply")
)}
</Button>
</div>
</CardContent>
</Card>
</div>
);
}