import { useState, useEffect, useCallback } from 'react'; import { useNotifications, NotificationSeverity } from '@/contexts/NotificationContext'; export type MetricType = | 'threat_count' | 'cpu_usage' | 'memory_usage' | 'network_traffic' | 'failed_logins' | 'active_connections' | 'response_time' | 'error_rate'; export type Operator = 'gt' | 'lt' | 'eq' | 'gte' | 'lte'; export interface AlertRule { id: string; name: string; description?: string; enabled: boolean; metric: MetricType; operator: Operator; threshold: number; severity: NotificationSeverity; cooldownMinutes: number; lastTriggered?: number; triggerCount: number; createdAt: number; } export interface MetricValue { metric: MetricType; value: number; timestamp: number; } const RULES_STORAGE_KEY = 'cyber-alert-rules'; export const METRIC_CONFIG: Record = { threat_count: { label: 'Antal trusler', unit: '', icon: 'Shield' }, cpu_usage: { label: 'CPU forbrug', unit: '%', icon: 'Cpu' }, memory_usage: { label: 'Memory forbrug', unit: '%', icon: 'HardDrive' }, network_traffic: { label: 'Netværkstrafik', unit: 'MB/s', icon: 'Network' }, failed_logins: { label: 'Fejlede logins', unit: '', icon: 'UserX' }, active_connections: { label: 'Aktive forbindelser', unit: '', icon: 'Users' }, response_time: { label: 'Responstid', unit: 'ms', icon: 'Clock' }, error_rate: { label: 'Fejlrate', unit: '%', icon: 'AlertCircle' }, }; export const OPERATOR_CONFIG: Record = { gt: { label: 'større end', symbol: '>' }, lt: { label: 'mindre end', symbol: '<' }, eq: { label: 'lig med', symbol: '=' }, gte: { label: 'større end eller lig', symbol: '≥' }, lte: { label: 'mindre end eller lig', symbol: '≤' }, }; // Simulated metric values for demo const generateMetricValue = (metric: MetricType): number => { switch (metric) { case 'threat_count': return Math.floor(Math.random() * 150); case 'cpu_usage': return Math.floor(Math.random() * 100); case 'memory_usage': return Math.floor(40 + Math.random() * 50); case 'network_traffic': return Math.floor(Math.random() * 500); case 'failed_logins': return Math.floor(Math.random() * 20); case 'active_connections': return Math.floor(50 + Math.random() * 200); case 'response_time': return Math.floor(50 + Math.random() * 300); case 'error_rate': return Math.floor(Math.random() * 15); default: return 0; } }; const evaluateCondition = (value: number, operator: Operator, threshold: number): boolean => { switch (operator) { case 'gt': return value > threshold; case 'lt': return value < threshold; case 'eq': return value === threshold; case 'gte': return value >= threshold; case 'lte': return value <= threshold; default: return false; } }; export const useAlertRules = () => { const [rules, setRules] = useState([]); const [currentMetrics, setCurrentMetrics] = useState>({} as any); const { addNotification } = useNotifications(); // Load rules from localStorage useEffect(() => { const saved = localStorage.getItem(RULES_STORAGE_KEY); if (saved) { try { setRules(JSON.parse(saved)); } catch (e) { console.error('Failed to load alert rules:', e); } } }, []); // Save rules to localStorage const saveRules = useCallback((newRules: AlertRule[]) => { localStorage.setItem(RULES_STORAGE_KEY, JSON.stringify(newRules)); setRules(newRules); }, []); // Create new rule const createRule = useCallback((rule: Omit): AlertRule => { const newRule: AlertRule = { ...rule, id: `rule-${Date.now()}`, triggerCount: 0, createdAt: Date.now(), }; saveRules([...rules, newRule]); return newRule; }, [rules, saveRules]); // Update existing rule const updateRule = useCallback((ruleId: string, updates: Partial) => { saveRules(rules.map(r => r.id === ruleId ? { ...r, ...updates } : r)); }, [rules, saveRules]); // Delete rule const deleteRule = useCallback((ruleId: string) => { saveRules(rules.filter(r => r.id !== ruleId)); }, [rules, saveRules]); // Toggle rule enabled state const toggleRule = useCallback((ruleId: string) => { saveRules(rules.map(r => r.id === ruleId ? { ...r, enabled: !r.enabled } : r)); }, [rules, saveRules]); // Evaluate all rules against current metrics const evaluateRules = useCallback((metrics: Record) => { const now = Date.now(); const updatedRules = [...rules]; let hasChanges = false; rules.forEach((rule, index) => { if (!rule.enabled) return; const metricValue = metrics[rule.metric]; if (metricValue === undefined) return; // Check cooldown if (rule.lastTriggered) { const cooldownMs = rule.cooldownMinutes * 60 * 1000; if (now - rule.lastTriggered < cooldownMs) return; } // Evaluate condition if (evaluateCondition(metricValue, rule.operator, rule.threshold)) { const config = METRIC_CONFIG[rule.metric]; const opConfig = OPERATOR_CONFIG[rule.operator]; addNotification({ title: `Alert: ${rule.name}`, message: `${config.label} er ${metricValue}${config.unit} (${opConfig.symbol} ${rule.threshold}${config.unit})`, severity: rule.severity, source: 'Alert Rules Engine', }); updatedRules[index] = { ...rule, lastTriggered: now, triggerCount: rule.triggerCount + 1, }; hasChanges = true; } }); if (hasChanges) { saveRules(updatedRules); } }, [rules, addNotification, saveRules]); // Simulate metric updates (for demo purposes) useEffect(() => { const updateMetrics = () => { const newMetrics: Record = { threat_count: generateMetricValue('threat_count'), cpu_usage: generateMetricValue('cpu_usage'), memory_usage: generateMetricValue('memory_usage'), network_traffic: generateMetricValue('network_traffic'), failed_logins: generateMetricValue('failed_logins'), active_connections: generateMetricValue('active_connections'), response_time: generateMetricValue('response_time'), error_rate: generateMetricValue('error_rate'), }; setCurrentMetrics(newMetrics); evaluateRules(newMetrics); }; // Initial update updateMetrics(); // Update every 15 seconds const interval = setInterval(updateMetrics, 15000); return () => clearInterval(interval); }, [evaluateRules]); return { rules, currentMetrics, createRule, updateRule, deleteRule, toggleRule, }; };