Kraft102's picture
fix: sql.js Docker/Alpine compatibility layer for PatternMemory and FailureMemory
5a81b95
import { useEffect, useState } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import {
Shield, AlertTriangle, Activity, TrendingUp, TrendingDown,
ExternalLink, RefreshCw, Clock, Globe, Server, Users,
CheckCircle, XCircle, AlertCircle, Eye, Lock, Wifi, Zap,
Info, Code, Database
} from 'lucide-react';
import { useSecuritySimulation, SecurityEvent } from '@/hooks/useSecuritySimulation';
import { WidgetSchemaViewer } from './WidgetSchemaViewer';
import { widgetRegistry } from '@/lib/widgetRegistry';
interface WidgetDetailModalProps {
isOpen: boolean;
onClose: () => void;
widget: {
id: string;
name: string;
category: string;
icon: React.ElementType;
component: React.ReactNode;
} | null;
}
// Widget descriptions
const widgetDescriptions: Record<string, string> = {
'soc-dashboard': 'Security Operations Center dashboard viser real-time oversigt over alle sikkerhedshændelser, trusler og systemstatus på tværs af infrastrukturen.',
'threat-map': 'Geografisk visualisering af aktive cyberangreb og trusselsaktivitet i realtid. Viser angrebskilder, mål og angrebstyper.',
'malware-detection': 'Real-time malware detektion og klassificering. Overvåger alle endpoints og netværkstrafik for kendte og ukendte trusler.',
'intrusion-detection': 'Intrusion Detection System (IDS) overvåger netværkstrafik for mistænkelig aktivitet og kendte angrebsmønstre.',
'ddos-monitor': 'DDoS beskyttelse og monitoring. Overvåger båndbredde, pakkerater og anvender automatisk mitigation ved angreb.',
'phishing-detector': 'Email sikkerhed med avanceret phishing detektion, URL analyse og attachment sandboxing.',
'dark-web-monitor': 'Overvåger dark web forums, markedspladser og paste sites for lækkede credentials og virksomhedsdata.',
'zero-day-alert': 'Overvåger for zero-day sårbarheder og nye CVEs der påvirker jeres infrastruktur.',
'threat-level': 'Samlet trusselsniveau baseret på global threat intelligence, interne alerts og systemstatus.',
'firewall': 'Next-generation firewall status med deep packet inspection, application control og intrusion prevention.',
'siem-alerts': 'Security Information and Event Management - korrelerer logs og events på tværs af hele infrastrukturen.',
'waf-status': 'Web Application Firewall beskytter web apps mod OWASP Top 10 og avancerede angreb.',
'vpn-status': 'VPN gateway status og connected users. Overvåger tunnels, performance og sikkerhed.',
'security-alerts': 'Samlet oversigt over alle kritiske sikkerhedsadvarsler der kræver øjeblikkelig handling.',
};
const widgetActions: Record<string, string[]> = {
'soc-dashboard': ['Eskalér til Tier 2', 'Bloker IP', 'Kør forensic scan'],
'threat-map': ['Aktivér GeoBlock', 'Rate Limiting', 'Kontakt ISP'],
'malware-detection': ['Isolér Endpoint', 'Kør Full Scan', 'Opdater Signatures'],
'intrusion-detection': ['Bloker Kilde IP', 'Opdater IDS Regler', 'Eksporter PCAP'],
'ddos-monitor': ['Aktivér Scrubbing', 'Whitelist IPs', 'Kontakt Upstream'],
'phishing-detector': ['Rapporter til CERT', 'Block Domain', 'Notify User'],
'dark-web-monitor': ['Force Password Reset', 'Notify Users', 'Enable MFA'],
'firewall': ['View Blocked IPs', 'Update Rules', 'Export Logs'],
'vpn-status': ['Disconnect User', 'View Sessions', 'Rotate Keys'],
};
// Type for metrics
type MetricStatus = 'good' | 'warning' | 'critical';
type MetricTrend = 'up' | 'down' | 'stable';
interface DynamicMetric {
label: string;
value: string;
status?: MetricStatus;
trend?: MetricTrend;
}
export const WidgetDetailModal = ({ isOpen, onClose, widget }: WidgetDetailModalProps) => {
const simulation = useSecuritySimulation(1500);
const [pulseMetric, setPulseMetric] = useState<number | null>(null);
// Pulse effect when metrics update
useEffect(() => {
if (isOpen) {
const randomMetric = Math.floor(Math.random() * 4);
setPulseMetric(randomMetric);
const timeout = setTimeout(() => setPulseMetric(null), 500);
return () => clearTimeout(timeout);
}
}, [simulation.socMetrics.activeIncidents, simulation.threatData.blockedAttacks, isOpen]);
if (!widget) return null;
const Icon = widget.icon;
const description = widgetDescriptions[widget.id] || 'Dette sikkerhedsmodul overvåger og beskytter din infrastruktur i realtid.';
const actions = widgetActions[widget.id] || ['View Details', 'Configure', 'Export Report'];
// Helper to get status
const getStatus = (condition: boolean, ifTrue: MetricStatus, ifFalse: MetricStatus): MetricStatus =>
condition ? ifTrue : ifFalse;
// Generate dynamic metrics based on widget type
const getDynamicMetrics = (): DynamicMetric[] => {
switch (widget.id) {
case 'soc-dashboard':
return [
{ label: 'Aktive Incidents', value: String(simulation.socMetrics.activeIncidents), status: getStatus(simulation.socMetrics.activeIncidents > 3, 'critical', 'warning') },
{ label: 'Løste (24h)', value: String(simulation.socMetrics.resolvedToday), status: 'good' as MetricStatus },
{ label: 'MTTD', value: `${simulation.socMetrics.mttd} min`, trend: 'down' as MetricTrend },
{ label: 'MTTR', value: `${simulation.socMetrics.mttr} min`, trend: 'down' as MetricTrend },
];
case 'threat-map':
return [
{ label: 'Live Angreb', value: String(simulation.liveAttacks), status: 'warning' as MetricStatus },
{ label: 'Blokerede IPs', value: simulation.networkMetrics.blockedIPs.toLocaleString(), status: 'good' as MetricStatus },
{ label: 'Top Kilde', value: 'CN (34%)', status: 'warning' as MetricStatus },
{ label: 'Traffic', value: `${simulation.networkMetrics.inboundGbps} Gbps`, trend: 'stable' as MetricTrend },
];
case 'malware-detection':
return [
{ label: 'Detekteret (24h)', value: String(simulation.malwareMetrics.detected24h), status: 'warning' as MetricStatus },
{ label: 'Ransomware', value: String(simulation.malwareMetrics.ransomware), status: getStatus(simulation.malwareMetrics.ransomware > 2, 'critical', 'warning') },
{ label: 'Trojans', value: String(simulation.malwareMetrics.trojans), status: 'warning' as MetricStatus },
{ label: 'Quarantined', value: String(simulation.malwareMetrics.quarantined), status: 'good' as MetricStatus },
];
case 'firewall':
return [
{ label: 'Status', value: simulation.firewallMetrics.status.toUpperCase(), status: 'good' as MetricStatus },
{ label: 'Blocked (24h)', value: simulation.firewallMetrics.blocked24h.toLocaleString(), status: 'good' as MetricStatus },
{ label: 'Allowed', value: simulation.firewallMetrics.allowed24h.toLocaleString(), trend: 'stable' as MetricTrend },
{ label: 'Rules', value: simulation.firewallMetrics.rulesActive.toLocaleString(), status: 'good' as MetricStatus },
];
case 'ddos-monitor': {
const ddosStatus: MetricStatus = simulation.networkMetrics.ddosStatus === 'normal' ? 'good' : simulation.networkMetrics.ddosStatus === 'elevated' ? 'warning' : 'critical';
return [
{ label: 'Status', value: simulation.networkMetrics.ddosStatus.toUpperCase(), status: ddosStatus },
{ label: 'Inbound', value: `${simulation.networkMetrics.inboundGbps} Gbps`, trend: 'stable' as MetricTrend },
{ label: 'Packets/sec', value: `${(simulation.networkMetrics.packetsPerSec / 1000000).toFixed(2)}M`, trend: 'stable' as MetricTrend },
{ label: 'Connections', value: simulation.networkMetrics.activeConnections.toLocaleString(), trend: 'up' as MetricTrend },
];
}
case 'vpn-status':
return [
{ label: 'Status', value: 'CONNECTED', status: 'good' as MetricStatus },
{ label: 'Users Online', value: String(simulation.vpnUsers), trend: 'stable' as MetricTrend },
{ label: 'Bandwidth', value: `${simulation.networkMetrics.outboundGbps} Gbps`, trend: 'up' as MetricTrend },
{ label: 'Tunnels', value: '12', status: 'good' as MetricStatus },
];
case 'threat-level': {
const threatStatus: MetricStatus = simulation.threatData.level === 'LOW' ? 'good' : simulation.threatData.level === 'MEDIUM' ? 'warning' : 'critical';
const threatTrend: MetricTrend = simulation.threatData.score > 50 ? 'up' : 'down';
return [
{ label: 'Threat Level', value: simulation.threatData.level, status: threatStatus },
{ label: 'Risk Score', value: `${simulation.threatData.score}/100`, trend: threatTrend },
{ label: 'Active Threats', value: String(simulation.threatData.activeThreats), status: getStatus(simulation.threatData.activeThreats > 2, 'warning', 'good') },
{ label: 'Blocked', value: simulation.threatData.blockedAttacks.toLocaleString(), status: 'good' as MetricStatus },
];
}
case 'security-alerts':
return [
{ label: 'Active Alerts', value: String(simulation.socMetrics.activeIncidents + simulation.socMetrics.criticalAlerts), status: 'critical' as MetricStatus },
{ label: 'Critical', value: String(simulation.socMetrics.criticalAlerts), status: 'critical' as MetricStatus },
{ label: 'Warning', value: String(simulation.socMetrics.warningAlerts), status: 'warning' as MetricStatus },
{ label: 'Info', value: String(simulation.socMetrics.infoAlerts), status: 'good' as MetricStatus },
];
default:
return [
{ label: 'Status', value: 'ACTIVE', status: 'good' as MetricStatus },
{ label: 'Last Check', value: '< 1 min', trend: 'stable' as MetricTrend },
{ label: 'Health', value: `${simulation.complianceScore}%`, status: 'good' as MetricStatus },
{ label: 'Uptime', value: '99.9%', status: 'good' as MetricStatus },
];
}
};
const metrics = getDynamicMetrics();
const getStatusColor = (status?: MetricStatus) => {
switch (status) {
case 'good': return 'text-green-400';
case 'warning': return 'text-yellow-400';
case 'critical': return 'text-red-400';
default: return 'text-primary';
}
};
const getSeverityBadge = (severity: 'info' | 'warning' | 'critical') => {
switch (severity) {
case 'critical': return <Badge variant="destructive" className="text-[8px] px-1 py-0 animate-pulse">CRITICAL</Badge>;
case 'warning': return <Badge variant="secondary" className="text-[8px] px-1 py-0 bg-yellow-500/20 text-yellow-400">WARNING</Badge>;
default: return <Badge variant="secondary" className="text-[8px] px-1 py-0 bg-blue-500/20 text-blue-400">INFO</Badge>;
}
};
const getTrendIcon = (trend?: 'up' | 'down' | 'stable') => {
switch (trend) {
case 'up': return <TrendingUp className="w-3 h-3 text-green-400" />;
case 'down': return <TrendingDown className="w-3 h-3 text-red-400" />;
default: return <Activity className="w-3 h-3 text-muted-foreground" />;
}
};
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-w-2xl bg-background/95 backdrop-blur-xl border-primary/30">
<DialogHeader>
<DialogTitle className="flex items-center gap-3 font-mono text-primary">
<div className="p-2 bg-primary/10 rounded-lg relative">
<Icon className="w-5 h-5" />
<div className="absolute -top-1 -right-1 w-2 h-2 bg-green-500 rounded-full animate-pulse" />
</div>
<div className="flex-1">
<span className="text-lg">{widget.name}</span>
<Badge variant="outline" className="ml-3 text-[10px]">{widget.category.toUpperCase()}</Badge>
</div>
<div className="flex items-center gap-2 text-xs text-green-400">
<Zap className="w-3 h-3" />
<span className="font-mono">LIVE</span>
</div>
</DialogTitle>
</DialogHeader>
{/* Description */}
<p className="text-sm text-muted-foreground leading-relaxed pt-2">
{description}
</p>
<Tabs defaultValue="live" className="w-full">
<TabsList className="grid w-full grid-cols-2 bg-secondary/30">
<TabsTrigger value="live" className="font-mono text-xs gap-2">
<Zap className="w-3 h-3" />
LIVE DATA
</TabsTrigger>
<TabsTrigger value="schema" className="font-mono text-xs gap-2">
<Code className="w-3 h-3" />
SCHEMA
</TabsTrigger>
</TabsList>
<TabsContent value="live" className="space-y-6 pt-4">
{/* Widget Preview */}
<div className="bg-secondary/20 rounded-lg p-4 border border-border/50 relative overflow-hidden">
<div className="absolute top-2 right-2 flex items-center gap-1 text-[10px] text-green-400 font-mono">
<div className="w-1.5 h-1.5 bg-green-400 rounded-full animate-pulse" />
REAL-TIME
</div>
<div className="h-32">
{widget.component}
</div>
</div>
{/* Metrics Grid - Live updating */}
<div>
<h4 className="text-xs font-mono text-muted-foreground mb-3 flex items-center gap-2">
<Activity className="w-3 h-3" />
KEY METRICS
<span className="text-green-400 text-[10px] ml-auto">● Live</span>
</h4>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
{metrics.map((metric, i) => (
<div
key={i}
className={`bg-secondary/30 rounded-lg p-3 border transition-all duration-300 ${
pulseMetric === i ? 'border-primary/50 shadow-[0_0_10px_rgba(var(--primary),0.3)]' : 'border-border/30'
}`}
>
<div className="text-[10px] text-muted-foreground mb-1">{metric.label}</div>
<div className="flex items-center justify-between">
<span className={`text-lg font-mono transition-all duration-300 ${getStatusColor(metric.status)}`}>
{metric.value}
</span>
{metric.trend && getTrendIcon(metric.trend)}
</div>
</div>
))}
</div>
</div>
{/* Recent Events - Live feed */}
<div>
<h4 className="text-xs font-mono text-muted-foreground mb-3 flex items-center gap-2">
<Clock className="w-3 h-3" />
LIVE EVENT FEED
<span className="text-green-400 text-[10px] ml-auto">● Live</span>
</h4>
<div className="space-y-2 max-h-40 overflow-y-auto">
{simulation.events.slice(0, 5).map((event, i) => (
<div
key={event.id}
className={`flex items-center gap-3 bg-secondary/20 rounded-lg p-2 border border-border/30 transition-all duration-500 ${
i === 0 ? 'animate-pulse border-primary/30' : ''
}`}
>
<span className="text-[10px] font-mono text-primary/70 w-16">{event.time}</span>
<span className="flex-1 text-xs text-muted-foreground truncate">{event.event}</span>
{getSeverityBadge(event.severity)}
</div>
))}
</div>
</div>
{/* Quick Actions */}
<div>
<h4 className="text-xs font-mono text-muted-foreground mb-3 flex items-center gap-2">
<Wifi className="w-3 h-3" />
QUICK ACTIONS
</h4>
<div className="flex flex-wrap gap-2">
{actions.map((action, i) => (
<Button key={i} variant="outline" size="sm" className="text-xs font-mono gap-2 hover:bg-primary/10 hover:border-primary/50 transition-colors">
{action}
<ExternalLink className="w-3 h-3" />
</Button>
))}
<Button variant="ghost" size="sm" className="text-xs font-mono gap-2 text-green-400 hover:text-green-300">
<RefreshCw className="w-3 h-3 animate-spin" style={{ animationDuration: '3s' }} />
Auto-Refresh ON
</Button>
</div>
</div>
</TabsContent>
<TabsContent value="schema" className="pt-4">
{/* Widget Schema / Metadata */}
<div className="space-y-4">
<div className="flex items-center gap-2 text-xs text-muted-foreground mb-2">
<Database className="w-3 h-3" />
<span>Widget metadata definerer hvad denne widget kan og kræver i økosystemet</span>
</div>
{widgetRegistry.get(widget.id) ? (
<WidgetSchemaViewer widgetId={widget.id} />
) : (
<div className="p-6 border border-dashed border-border/50 rounded-lg text-center">
<Info className="w-8 h-8 text-muted-foreground mx-auto mb-3" />
<p className="text-sm text-muted-foreground">
Schema metadata er endnu ikke defineret for denne widget.
</p>
<p className="text-xs text-muted-foreground/70 mt-2">
Tilføj widget til registret for at aktivere økosystem-integration.
</p>
</div>
)}
{widgetRegistry.get(widget.id) && (
<div className="mt-4 p-3 bg-primary/5 rounded-lg border border-primary/20">
<h5 className="text-xs font-mono text-primary mb-2 flex items-center gap-2">
<Globe className="w-3 h-3" />
KOMPATIBLE WIDGETS
</h5>
<div className="flex flex-wrap gap-2">
{widgetRegistry.findCompatible(widget.id).slice(0, 5).map(compatible => (
<Badge key={compatible.id} variant="secondary" className="text-[10px]">
{compatible.name}
</Badge>
))}
{widgetRegistry.findCompatible(widget.id).length === 0 && (
<span className="text-xs text-muted-foreground">Ingen kompatible widgets fundet</span>
)}
</div>
</div>
)}
</div>
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
);
};
export default WidgetDetailModal;