/**
* 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
};
};