import { useQuery, useMutation } from "@tanstack/react-query"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Slider } from "@/components/ui/slider"; import { AlertTriangle, Search, Shield, Globe, MapPin, Building2, ShieldPlus, ShieldCheck, Unlock, ChevronLeft, ChevronRight } from "lucide-react"; import { format } from "date-fns"; import { useState, useEffect, useMemo } from "react"; import type { Detection, Whitelist } from "@shared/schema"; import { getFlag } from "@/lib/country-flags"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { useToast } from "@/hooks/use-toast"; const ITEMS_PER_PAGE = 50; interface DetectionsResponse { detections: Detection[]; total: number; } export default function Detections() { const [searchInput, setSearchInput] = useState(""); const [debouncedSearch, setDebouncedSearch] = useState(""); const [anomalyTypeFilter, setAnomalyTypeFilter] = useState("all"); const [minScore, setMinScore] = useState(0); const [maxScore, setMaxScore] = useState(100); const [currentPage, setCurrentPage] = useState(1); const { toast } = useToast(); // Debounce search input useEffect(() => { const timer = setTimeout(() => { setDebouncedSearch(searchInput); setCurrentPage(1); // Reset to first page on search }, 300); return () => clearTimeout(timer); }, [searchInput]); // Reset page on filter change useEffect(() => { setCurrentPage(1); }, [anomalyTypeFilter, minScore, maxScore]); // Build query params with pagination and search const queryParams = useMemo(() => { const params = new URLSearchParams(); params.set("limit", ITEMS_PER_PAGE.toString()); params.set("offset", ((currentPage - 1) * ITEMS_PER_PAGE).toString()); if (anomalyTypeFilter !== "all") { params.set("anomalyType", anomalyTypeFilter); } if (minScore > 0) { params.set("minScore", minScore.toString()); } if (maxScore < 100) { params.set("maxScore", maxScore.toString()); } if (debouncedSearch.trim()) { params.set("search", debouncedSearch.trim()); } return params.toString(); }, [currentPage, anomalyTypeFilter, minScore, maxScore, debouncedSearch]); const { data, isLoading } = useQuery({ queryKey: ["/api/detections", currentPage, anomalyTypeFilter, minScore, maxScore, debouncedSearch], queryFn: () => fetch(`/api/detections?${queryParams}`).then(r => r.json()), refetchInterval: 10000, }); const detections = data?.detections || []; const totalCount = data?.total || 0; const totalPages = Math.ceil(totalCount / ITEMS_PER_PAGE); const { data: whitelistData } = useQuery<{ items: Whitelist[]; total: number }>({ queryKey: ["/api/whitelist", "all"], queryFn: () => fetch("/api/whitelist?limit=10000").then(r => r.json()), }); const whitelistedIps = new Set(whitelistData?.items?.map(w => w.ipAddress) || []); // Mutation per aggiungere a whitelist const addToWhitelistMutation = useMutation({ mutationFn: async (detection: Detection) => { return await apiRequest("POST", "/api/whitelist", { ipAddress: detection.sourceIp, reason: `Auto-added from detection: ${detection.anomalyType} (Risk: ${parseFloat(detection.riskScore).toFixed(1)})` }); }, onSuccess: (_, detection) => { toast({ title: "IP aggiunto alla whitelist", description: `${detection.sourceIp} è stato aggiunto alla whitelist e sbloccato dai router.`, }); queryClient.invalidateQueries({ queryKey: ["/api/whitelist"] }); queryClient.invalidateQueries({ queryKey: ["/api/detections"] }); }, onError: (error: any, detection) => { toast({ title: "Errore", description: error.message || `Impossibile aggiungere ${detection.sourceIp} alla whitelist.`, variant: "destructive", }); } }); // Mutation per sbloccare IP dai router const unblockMutation = useMutation({ mutationFn: async (detection: Detection) => { return await apiRequest("POST", "/api/unblock-ip", { ipAddress: detection.sourceIp }); }, onSuccess: (data: any, detection) => { toast({ title: "IP sbloccato", description: `${detection.sourceIp} è stato rimosso dalla blocklist di ${data.unblocked_from || 0} router.`, }); queryClient.invalidateQueries({ queryKey: ["/api/detections"] }); }, onError: (error: any, detection) => { toast({ title: "Errore sblocco", description: error.message || `Impossibile sbloccare ${detection.sourceIp} dai router.`, variant: "destructive", }); } }); const getRiskBadge = (riskScore: string) => { const score = parseFloat(riskScore); if (score >= 85) return CRITICO; if (score >= 70) return ALTO; if (score >= 60) return MEDIO; if (score >= 40) return BASSO; return NORMALE; }; const getAnomalyTypeLabel = (type: string) => { const labels: Record = { ddos: "DDoS Attack", port_scan: "Port Scanning", brute_force: "Brute Force", botnet: "Botnet Activity", suspicious: "Suspicious Activity" }; return labels[type] || type; }; return (

Rilevamenti

Anomalie rilevate dal sistema IDS

{/* Search and Filters */}
setSearchInput(e.target.value)} className="pl-9" data-testid="input-search" />
Risk Score: {minScore} - {maxScore}
0 { setMinScore(min); setMaxScore(max); }} className="flex-1" data-testid="slider-risk-score" /> 100
{/* Detections List */}
Rilevamenti ({totalCount})
{totalPages > 1 && (
Pagina {currentPage} di {totalPages}
)}
{isLoading ? (
Caricamento...
) : detections.length > 0 ? (
{detections.map((detection) => (
{/* Flag Emoji */} {detection.countryCode && ( {getFlag(detection.country, detection.countryCode)} )} {detection.sourceIp} {getRiskBadge(detection.riskScore)} {getAnomalyTypeLabel(detection.anomalyType)}

{detection.reason}

{/* Geolocation Info */} {(detection.country || detection.organization || detection.asNumber) && (
{detection.country && (
{detection.city ? `${detection.city}, ${detection.country}` : detection.country}
)} {detection.organization && (
{detection.organization}
)} {detection.asNumber && (
{detection.asNumber} {detection.asName && `- ${detection.asName}`}
)}
)}

Risk Score

{parseFloat(detection.riskScore).toFixed(1)}/100

Confidence

{parseFloat(detection.confidence).toFixed(1)}%

Log Count

{detection.logCount}

Rilevato

{format(new Date(detection.detectedAt), "dd/MM HH:mm")}

Prima: {format(new Date(detection.firstSeen), "dd/MM HH:mm:ss")} Ultima: {format(new Date(detection.lastSeen), "dd/MM HH:mm:ss")}
{detection.blocked ? ( Bloccato ) : ( Attivo )} {whitelistedIps.has(detection.sourceIp) ? ( ) : ( )} {detection.blocked && ( )}
))}
) : (

Nessun rilevamento trovato

{debouncedSearch && (

Prova con un altro termine di ricerca

)}
)} {/* Bottom pagination */} {totalPages > 1 && detections.length > 0 && (
Pagina {currentPage} di {totalPages} ({totalCount} totali)
)}
); }