/** * Audit Tasks Page * Cyberpunk Terminal Aesthetic * 支持普通审计任务和Agent审计任务 */ import { useState, useEffect, useRef } from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Progress } from "@/components/ui/progress"; import { Activity, AlertTriangle, CheckCircle, Clock, Search, FileText, Calendar, Plus, XCircle, ArrowUpRight, Shield, Terminal, Bot, Zap } from "lucide-react"; import { api } from "@/shared/config/database"; import type { AuditTask } from "@/shared/types"; import { Link, useNavigate } from "react-router-dom"; import { toast } from "sonner"; import CreateTaskDialog from "@/components/audit/CreateTaskDialog"; import TerminalProgressDialog from "@/components/audit/TerminalProgressDialog"; import { calculateTaskProgress } from "@/shared/utils/utils"; import { getAgentTasks, cancelAgentTask, type AgentTask } from "@/shared/api/agentTasks"; // Zombie task detection config const ZOMBIE_TIMEOUT = 180000; // 3 minutes without progress is potentially stuck // 任务类型标签 type TaskTab = "regular" | "agent"; export default function AuditTasks() { const navigate = useNavigate(); const [activeTab, setActiveTab] = useState("agent"); // 默认显示Agent任务 // 普通任务状态 const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); const [showCreateDialog, setShowCreateDialog] = useState(false); const [cancellingTaskId, setCancellingTaskId] = useState(null); const [showTerminal, setShowTerminal] = useState(false); const [currentTaskId, setCurrentTaskId] = useState(null); // Agent任务状态 const [agentTasks, setAgentTasks] = useState([]); const [agentLoading, setAgentLoading] = useState(true); const [cancellingAgentTaskId, setCancellingAgentTaskId] = useState(null); // Zombie task detection: track progress and time for each task const taskProgressRef = useRef>(new Map()); useEffect(() => { loadTasks(); loadAgentTasks(); }, []); // 加载Agent任务(支持静默更新,不触发 loading 状态) const loadAgentTasks = async (silent = false) => { try { if (!silent) { setAgentLoading(true); } const data = await getAgentTasks(); setAgentTasks(data); } catch (error) { console.error('Failed to load agent tasks:', error); if (!silent) { toast.error("加载Agent任务失败"); } } finally { if (!silent) { setAgentLoading(false); } } }; // Silently update active tasks progress (no loading state trigger) useEffect(() => { const activeTasks = tasks.filter( task => task.status === 'running' || task.status === 'pending' ); if (activeTasks.length === 0) { taskProgressRef.current.clear(); return; } const intervalId = setInterval(async () => { try { const updatedData = await api.getAuditTasks(); setTasks(prevTasks => { return prevTasks.map(prevTask => { const updated = updatedData.find(t => t.id === prevTask.id); if (!updated) return prevTask; // Zombie task detection if (updated.status === 'running') { const currentProgress = updated.scanned_files || 0; const lastRecord = taskProgressRef.current.get(updated.id); if (lastRecord) { if (currentProgress !== lastRecord.progress) { taskProgressRef.current.set(updated.id, { progress: currentProgress, time: Date.now() }); } else if (Date.now() - lastRecord.time > ZOMBIE_TIMEOUT) { toast.warning(`任务 "${updated.project?.name || '未知'}" 可能已停止响应`, { id: `zombie-${updated.id}`, duration: 10000, action: { label: '取消任务', onClick: () => handleCancelTask(updated.id), }, }); taskProgressRef.current.set(updated.id, { progress: currentProgress, time: Date.now() }); } } else { taskProgressRef.current.set(updated.id, { progress: currentProgress, time: Date.now() }); } } else { taskProgressRef.current.delete(updated.id); } if ( updated.status !== prevTask.status || updated.scanned_files !== prevTask.scanned_files || updated.issues_count !== prevTask.issues_count ) { return updated; } return prevTask; }); }); } catch (error) { console.error('静默更新任务列表失败:', error); toast.error("获取任务状态失败,请检查网络连接", { id: 'network-error', duration: 5000, }); } }, 3000); return () => clearInterval(intervalId); }, [tasks.map(t => t.id + t.status).join(',')]); // 自动刷新Agent任务(静默更新,不显示 loading) useEffect(() => { const activeAgentTasks = agentTasks.filter( task => task.status === 'running' || task.status === 'pending' ); if (activeAgentTasks.length === 0) return; const intervalId = setInterval(() => loadAgentTasks(true), 5000); return () => clearInterval(intervalId); }, [agentTasks.map(t => t.id + t.status).join(',')]); const handleCancelTask = async (taskId: string) => { if (cancellingTaskId) return; try { setCancellingTaskId(taskId); await api.cancelAuditTask(taskId); toast.success("任务已取消"); await loadTasks(); } catch (error: any) { console.error('取消任务失败:', error); toast.error(error?.response?.data?.detail || "取消任务失败"); } finally { setCancellingTaskId(null); } }; const handleCancelAgentTask = async (taskId: string) => { if (cancellingAgentTaskId) return; try { setCancellingAgentTaskId(taskId); await cancelAgentTask(taskId); toast.success("Agent任务已取消"); // 取消后刷新列表,不使用静默模式以显示最新状态 await loadAgentTasks(false); } catch (error: any) { console.error('取消Agent任务失败:', error); toast.error(error?.response?.data?.detail || "取消Agent任务失败"); } finally { setCancellingAgentTaskId(null); } }; const loadTasks = async () => { try { setLoading(true); const data = await api.getAuditTasks(); setTasks(data); } catch (error) { console.error('Failed to load tasks:', error); toast.error("加载任务失败"); } finally { setLoading(false); } }; const handleFastScanStarted = (taskId: string) => { setCurrentTaskId(taskId); setShowTerminal(true); }; const getStatusBadge = (status: string) => { switch (status) { case 'completed': return 完成; case 'running': return 运行中; case 'failed': return 失败; case 'cancelled': return 已取消; default: return 等待中; } }; const getStatusIcon = (status: string) => { switch (status) { case 'completed': return ; case 'running': return ; case 'failed': return ; case 'cancelled': return ; default: return ; } }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('zh-CN', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); }; const filteredTasks = tasks.filter(task => { const matchesSearch = task.project?.name.toLowerCase().includes(searchTerm.toLowerCase()) || task.task_type.toLowerCase().includes(searchTerm.toLowerCase()); const matchesStatus = statusFilter === "all" || task.status === statusFilter; return matchesSearch && matchesStatus; }); const filteredAgentTasks = agentTasks.filter(task => { const matchesSearch = (task.name || '').toLowerCase().includes(searchTerm.toLowerCase()) || task.task_type.toLowerCase().includes(searchTerm.toLowerCase()); const matchesStatus = statusFilter === "all" || task.status === statusFilter; return matchesSearch && matchesStatus; }); // 统计数据 const regularStats = { total: tasks.length, completed: tasks.filter(t => t.status === 'completed').length, running: tasks.filter(t => t.status === 'running').length, failed: tasks.filter(t => t.status === 'failed').length, }; const agentStats = { total: agentTasks.length, completed: agentTasks.filter(t => t.status === 'completed').length, running: agentTasks.filter(t => t.status === 'running').length, failed: agentTasks.filter(t => t.status === 'failed').length, }; const currentStats = activeTab === "agent" ? agentStats : regularStats; if ((activeTab === "regular" && loading) || (activeTab === "agent" && agentLoading)) { return (

加载任务数据...

); } return (
{/* Grid background */}
{/* Tab 切换 - 卡片式设计 */}
{/* Agent任务卡片 */} {/* 快速扫描任务卡片 */}
{/* Stats Cards */}

总任务数

{currentStats.total}

已完成

{currentStats.completed}

运行中

{currentStats.running}

失败

{currentStats.failed}

{/* Search and Filter */}
setSearchTerm(e.target.value)} className="cyber-input !pl-10" />
{activeTab === "regular" && ( )} {activeTab === "agent" && ( )}
{/* Agent Task List */} {activeTab === "agent" && ( <> {filteredAgentTasks.length > 0 ? (
{filteredAgentTasks.map((task) => (
{/* Task Header */}

{task.name || 'Agent审计任务'}

{task.current_phase || task.task_type}

{getStatusBadge(task.status)} {task.status === 'running' && (
)}
{/* Stats Grid */}

{task.total_files}

文件数

{task.analyzed_files}

已分析

{task.findings_count}

发现问题

{task.tool_calls_count || 0}

工具调用

{task.security_score?.toFixed(1) || '-'}

安全评分

{/* Severity Distribution */} {task.findings_count > 0 && (
{task.critical_count > 0 && ( Critical: {task.critical_count} )} {task.high_count > 0 && ( High: {task.high_count} )} {task.medium_count > 0 && ( Medium: {task.medium_count} )} {task.low_count > 0 && ( Low: {task.low_count} )}
)} {/* Progress Bar */}
审计进度 {task.analyzed_files || 0} / {task.total_files || 0} 文件
{(task.progress_percentage || 0).toFixed(0)}% 完成
{/* Task Footer */}
{formatDate(task.created_at)}
{task.completed_at && (
{formatDate(task.completed_at)}
)} {task.tokens_used > 0 && (
{task.tokens_used.toLocaleString()} tokens
)}
{(task.status === 'running' || task.status === 'pending') && ( <> {/* 🔥 查看终端实时流按钮 */} )} {/* 任务详情按钮 */}
))}
) : (

{searchTerm || statusFilter !== "all" ? '未找到匹配的Agent任务' : '暂无Agent审计任务'}

{searchTerm || statusFilter !== "all" ? '尝试调整搜索条件或筛选器' : '创建第一个Agent审计任务开始智能安全审计'}

{!searchTerm && statusFilter === "all" && ( )}
)} )} {/* Regular Task List */} {activeTab === "regular" && ( <> {filteredTasks.length > 0 ? (
{filteredTasks.map((task) => (
{/* Task Header */}
{getStatusIcon(task.status)}

{task.project?.name || '未知项目'}

{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}

{getStatusBadge(task.status)}
{/* Stats Grid */}

{task.total_files}

文件数

{task.total_lines.toLocaleString()}

代码行数

{task.issues_count}

发现问题

{task.quality_score.toFixed(1)}

质量评分

{/* Progress Bar */}
扫描进度 {task.scanned_files || 0} / {task.total_files || 0} 文件
{calculateTaskProgress(task.scanned_files, task.total_files)}% 完成
{/* Task Footer */}
{formatDate(task.created_at)}
{task.completed_at && (
{formatDate(task.completed_at)}
)}
{(task.status === 'running' || task.status === 'pending') && ( )} {task.project && ( )}
))}
) : (

{searchTerm || statusFilter !== "all" ? '未找到匹配的任务' : '暂无审计任务'}

{searchTerm || statusFilter !== "all" ? '尝试调整搜索条件或筛选器' : '创建第一个审计任务开始代码质量分析'}

{!searchTerm && statusFilter === "all" && ( )}
)} )} {/* Create Task Dialog */} {/* Terminal Progress Dialog for Fast Scan */}
); }