aegisai / frontend / src / hooks / useMonitoring.ts
useMonitoring.ts
Raw
/**
 * useMonitoring Hook
 * Encapsulates all monitoring logic and state management
 */

import { useState, useCallback, useRef, useEffect } from 'react';
import { analyzeFrame } from '../services/geminiService';
import { SecurityEvent, SystemStats, ThreatSeverity } from '../types';

interface UseMonitoringReturn {
  isMonitoring: boolean;
  backendConnected: boolean; // ✅ added
  events: SecurityEvent[];
  currentAnalysis: SecurityEvent | null;
  stats: SystemStats;
  toggleMonitoring: () => void;
  handleFrameCapture: (base64: string) => Promise<void>;
  clearEvents: () => void;
  resetStats: () => void;
}

export const useMonitoring = (): UseMonitoringReturn => {
  const [isMonitoring, setIsMonitoring] = useState(false);
  const [backendConnected, setBackendConnected] = useState(true); // ✅ added
  const [events, setEvents] = useState<SecurityEvent[]>([]);
  const [currentAnalysis, setCurrentAnalysis] = useState<SecurityEvent | null>(null);
  const [stats, setStats] = useState<SystemStats>({
    scansPerformed: 0,
    incidentsDetected: 0,
    lastScanTime: Date.now(),
    uptime: 0,
    cpuUsage: 12,
    memoryUsage: 24
  });

  const audioContextRef = useRef<AudioContext | null>(null);
  const startTimeRef = useRef<number>(Date.now());

  // Initialize audio context
  useEffect(() => {
    audioContextRef.current = new (window.AudioContext || (window as any).webkitAudioContext)();
    
    return () => {
      audioContextRef.current?.close();
    };
  }, []);

  // Update uptime
  useEffect(() => {
    if (!isMonitoring) return;

    const interval = setInterval(() => {
      setStats(prev => ({
        ...prev,
        uptime: Math.floor((Date.now() - startTimeRef.current) / 1000)
      }));
    }, 1000);

    return () => clearInterval(interval);
  }, [isMonitoring]);

  const playAlert = useCallback(() => {
    const ctx = audioContextRef.current;
    if (!ctx) return;

    const oscillator = ctx.createOscillator();
    const gainNode = ctx.createGain();
    
    oscillator.connect(gainNode);
    gainNode.connect(ctx.destination);
    
    oscillator.frequency.value = 800;
    oscillator.type = 'sawtooth';
    gainNode.gain.value = 0.1;
    
    oscillator.start();
    oscillator.stop(ctx.currentTime + 0.5);
  }, []);

  const handleFrameCapture = useCallback(async (base64: string) => {
    if (!isMonitoring) return;

    // Update scan stats
    setStats(prev => ({
      ...prev,
      scansPerformed: prev.scansPerformed + 1,
      lastScanTime: Date.now(),
      cpuUsage: Math.min(100, Math.floor(Math.random() * 40) + 40)
    }));

    try {
      const result = await analyzeFrame(base64);

      // backend reachable
      setBackendConnected(true);

      if (result) {
        const newEvent: SecurityEvent = {
          id: crypto.randomUUID(),
          timestamp: Date.now(),
          incident: result.incident,
          type: result.type,
          severity: result.severity as ThreatSeverity,
          confidence: result.confidence,
          reasoning: result.reasoning,
          recommended_actions: result.recommended_actions,
          snapshot: base64
        };

        setCurrentAnalysis(newEvent);

        if (result.incident) {
          setEvents(prev => [...prev, newEvent]);
          setStats(prev => ({
            ...prev,
            incidentsDetected: prev.incidentsDetected + 1
          }));

          // Play alert for high severity
          if (result.severity === 'high' || result.severity === 'critical') {
            playAlert();
          }
        }
      }
    } catch (error) {
      console.error('Frame analysis error:', error);

      // backend unreachable
      setBackendConnected(false); // ✅ logical fallback
    } finally {
      // CPU settle
      setTimeout(() => {
        setStats(prev => ({
          ...prev,
          cpuUsage: Math.floor(Math.random() * 15) + 10
        }));
      }, 500);
    }
  }, [isMonitoring, playAlert]);

  const toggleMonitoring = useCallback(() => {
    setIsMonitoring(prev => {
      const newState = !prev;
      
      if (newState) {
        // Starting monitoring
        startTimeRef.current = Date.now();
      }
      
      return newState;
    });
  }, []);

  const clearEvents = useCallback(() => {
    setEvents([]);
    setCurrentAnalysis(null);
  }, []);

  const resetStats = useCallback(() => {
    setStats({
      scansPerformed: 0,
      incidentsDetected: 0,
      lastScanTime: Date.now(),
      uptime: 0,
      cpuUsage: 12,
      memoryUsage: 24
    });
    startTimeRef.current = Date.now();
  }, []);

  return {
    isMonitoring,
    backendConnected, // ✅ now valid
    events,
    currentAnalysis,
    stats,
    toggleMonitoring,
    handleFrameCapture,
    clearEvents,
    resetStats
  };
};