Files
aiops/frontend/src/components/system/system-settings/SecuritySettingsTab.tsx
T
attacker 8a4dfb0db6 refactor(quality): Q8 前端巨型组件拆分 — SystemSettingsTab 754L→173L (-77%), 提取通用/安全/LDAP 3 个独立子 Tab 组件
- GeneralSettingsTab.tsx (154L): 通用设置 Tab(审计策略 + 系统信息)
- SecuritySettingsTab.tsx (309L): 安全设置 Tab(安全策略 + 会话设置 9 项开关)
- LdapSettingsTab.tsx (243L): LDAP 设置 Tab(配置表单 + 管理区转发)
- 主文件仅保留 Props 接口 + Tab 路由 + 分发逻辑(173L)
- 每个子 Tab 用 Pick<SystemSettingsTabProps,...> 精确声明 props 子集
- tsc -b=0, npm build✓(17.71s), ESLint errors=0/warnings=59(无新增)
- 17/17 集成回归测试通过, Docker 部署 healthy
2026-07-07 01:05:07 +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">30分钟</SelectItem>
<SelectItem value="60">
{t("monitor.traceMgr.hour1")}
</SelectItem>
<SelectItem value="120">2小时</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>
);
}