refactor(frontend): P1-P2 架构统一 — utils/hooks/api 模块化

P1:
- utils/storage.ts、theme.ts、validate.ts + index 统一出口
- request/store/useTheme 接入 storage;清零 confirm/alert
- PipelineManager、TerminalAuditTab 拆分子模块(<700行红线)

P2:
- api/monitor、api/deploy 子目录化
- hooks/api 扩展:servers/pipelines/alerts/audit/sessionCommands
- PipelineManager、AuditOperationsTab 接入 TanStack Query
This commit is contained in:
Logan.Li committed 2026-06-26 00:18:14 +08:00
1 parent d19a03d427
commit f45e203e21
196 files changed
+3228 -2018

No files matched your search

@@ -35,6 +35,7 @@ import {
import { kubernetesApi } from '@/api';
import { useToast } from '@/hooks/use-toast';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { PodExecTerminal } from './PodExecTerminal';
import { PodLogsViewer } from './PodLogsViewer';
import { PodFileBrowser } from './PodFileBrowser';
@@ -48,6 +49,7 @@ interface DeploymentDetailProps {
export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBack }: DeploymentDetailProps) {
const { toast } = useToast();
const confirm = useConfirm();
const [deployment, setDeployment] = useState<any>(null);
const [pods, setPods] = useState<any[]>([]);
const [hpas, setHpas] = useState<any[]>([]);
@@ -174,7 +176,13 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
const handleHpaToggle = async ( hpa: any) => {
// Not direct toggle — delete HPA
if (!confirm(`确定删除 HPA "${hpa.name}"?`)) return;
const ok = await confirm({
title: '删除 HPA',
description: `确定删除 HPA "${hpa.name}"?`,
confirmText: '删除',
variant: 'destructive',
});
if (!ok) return;
try {
await kubernetesApi.deleteHPA(clusterCode, namespace, hpa.name);
toast({ title: 'HPA 已删除' });
@@ -23,6 +23,7 @@ import {
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { kubernetesApi } from '@/api';
interface Props {
@@ -72,6 +73,7 @@ const toArray = ( v: any) => (Array.isArray(v) ? v : (v?.list || v?.items || v?.
export function DeploymentEditService({ clusterCode, namespace, deploymentName, manifest, onSaved, onCancel }: Props) {
const { toast } = useToast();
const confirm = useConfirm();
const [loading, setLoading] = useState(true);
const [services, setServices] = useState<SvcSummary[]>([]);
const [ingresses, setIngresses] = useState<IngSummary[]>([]);
@@ -265,7 +267,13 @@ export function DeploymentEditService({ clusterCode, namespace, deploymentName,
// 删除资源
const deleteResource = async (kind: 'service' | 'ingress', name: string) => {
if (!window.confirm(`确认删除 ${kind === 'service' ? 'Service' : 'Ingress'} 「${name}」?此操作不可撤销。`)) return;
const ok = await confirm({
title: '删除资源',
description: `确认删除 ${kind === 'service' ? 'Service' : 'Ingress'} 「${name}」?此操作不可撤销。`,
confirmText: '删除',
variant: 'destructive',
});
if (!ok) return;
try {
const resource = kind === 'service' ? 'services' : 'ingresses';
await kubernetesApi.deleteResourceYaml(clusterCode, namespace, resource, name);
@@ -32,6 +32,7 @@ import {
import { kubernetesApi } from '@/api';
import { useToast } from '@/hooks/use-toast';
import { useConfirm } from '@/components/ui/confirm-dialog';
import {
formatK8sTime, formatDuration, elapsedSeconds, statusColor, STATUS_COLORS,
} from './k8s-yaml-utils';
@@ -76,6 +77,7 @@ function getPodTimeline(pod: PodItem) {
export function DeploymentView(props: DeploymentViewProps) {
const { clusterCode, namespace, deployment, pods, revisions, onRefresh } = props;
const { toast } = useToast();
const confirm = useConfirm();
const [tab, setTab] = useState('pods');
const [expandedPod, setExpandedPod] = useState<string | null>(null);
const [rollbackLoading, setRollbackLoading] = useState<number | null>(null);
@@ -190,7 +192,13 @@ export function DeploymentView(props: DeploymentViewProps) {
// Rollback
const handleRollback = async ( rev: any) => {
const revision = rev.revision ?? rev.revision_number ?? rev.number;
if (!confirm(`确认回滚到修订版本 #${revision}?`)) return;
const ok = await confirm({
title: '回滚版本',
description: `确认回滚到修订版本 #${revision}?`,
confirmText: '回滚',
variant: 'destructive',
});
if (!ok) return;
setRollbackLoading(revision);
try {
await kubernetesApi.rollbackDeployment(clusterCode, namespace, deployment.name, Number(revision));
@@ -36,6 +36,7 @@ import {
import { kubernetesApi } from '@/api';
import { useToast } from '@/hooks/use-toast';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { useUserStore } from '@/store';
import { YamlViewer } from './YamlViewer';
import { DeploymentView } from './DeploymentView';
@@ -73,6 +74,7 @@ type ActionModal =
export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mode, onBack, onModeChange }: DeploymentWorkloadProps) {
const navigate = useNavigate();
const { toast } = useToast();
const confirm = useConfirm();
const [deployment, setDeployment] = useState<any>(null);
const [manifest, setManifest] = useState<any>(null);
const [pods, setPods] = useState<any[]>([]);
@@ -180,7 +182,12 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
};
const handleRestart = async () => {
if (!confirm(`确认滚动重启 Deployment "${deploymentName}"?`)) return;
const ok = await confirm({
title: '滚动重启',
description: `确认滚动重启 Deployment "${deploymentName}"?`,
confirmText: '重启',
});
if (!ok) return;
setActionLoading('restart');
try {
await kubernetesApi.restartDeployment(clusterCode, namespace, deploymentName);
@@ -194,7 +201,12 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
const handleTogglePause = async () => {
const isPaused = !!deployment?.paused;
const action = isPaused ? '恢复' : '暂停';
if (!confirm(`确认${action} Deployment "${deploymentName}" 的滚动更新?`)) return;
const ok = await confirm({
title: `${action}滚动更新`,
description: `确认${action} Deployment "${deploymentName}" 的滚动更新?`,
confirmText: action,
});
if (!ok) return;
setActionLoading(isPaused ? 'resume' : 'pause');
try {
if (isPaused) {
@@ -16,6 +16,7 @@ import {
Shield, Activity, AlertTriangle, CheckCircle2,
} from 'lucide-react';
import { kubernetesApi } from '@/api';
import { toast } from 'sonner';
interface NamespaceDetail {
name: string;
@@ -75,7 +76,7 @@ export default function NamespaceManagementPage() {
setCreateOpen(false);
await loadData();
} catch (err: any) {
alert(err?.message || '创建失败');
toast.error(err?.message || '创建失败');
} finally {
setCreating(false);
}
@@ -89,7 +90,7 @@ export default function NamespaceManagementPage() {
setDeleteTarget(null);
await loadData();
} catch (err: any) {
alert(err?.message || '删除失败');
toast.error(err?.message || '删除失败');
} finally {
setDeleting(false);
}
@@ -15,6 +15,7 @@ import {
} from 'lucide-react';
import { kubernetesApi } from '@/api';
import { useToast } from '@/hooks/use-toast';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { YamlViewer } from './YamlViewer';
import { PodExecTerminal } from './PodExecTerminal';
import { PodFileBrowser } from './PodFileBrowser';
@@ -55,6 +56,7 @@ export default function WorkloadViewPage() {
const navigate = useNavigate();
const location = useLocation();
const { toast } = useToast();
const confirm = useConfirm();
const clusterCode = params.clusterCode || '';
const namespace = params.namespace || '';
@@ -114,7 +116,12 @@ export default function WorkloadViewPage() {
};
const handleRestart = async () => {
if (!confirm(`确认重启 ${kind} ${name}?\n将触发滚动重启。`)) return;
const ok = await confirm({
title: '滚动重启',
description: `确认重启 ${kind} ${name}?将触发滚动重启。`,
confirmText: '重启',
});
if (!ok) return;
setActionLoading(true);
try {
if (kind === 'StatefulSet') {
@@ -15,11 +15,13 @@ import {
import { ContainerEditor } from './ContainerEditor';
import type { ContainerGroupSectionProps } from './types';
import { useConfirm } from '@/components/ui/confirm-dialog';
export function ContainerGroupSection({
title, description, icon, containers, accentColor,
onAdd, onRemove, onUpdate, onDuplicate,
}: ContainerGroupSectionProps) {
const confirm = useConfirm();
const [openIndices, setOpenIndices] = useState<Set<number>>(new Set([0]));
const toggleOpen = (idx: number) => {
setOpenIndices(prev => {
@@ -70,7 +72,15 @@ export function ContainerGroupSection({
</Button>
)}
<Button variant="ghost" size="icon" className="h-7 w-7 text-red-400 hover:text-red-300" title="删除"
onClick={() => { if (confirm(`确认删除容器「${c.name}」?`)) onRemove(idx); }}>
onClick={async () => {
const ok = await confirm({
title: '删除容器',
description: `确认删除容器「${c.name}」?`,
confirmText: '删除',
variant: 'destructive',
});
if (ok) onRemove(idx);
}}>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>