import { useState, useEffect } from 'react'; import { getViewAsHeaders } from './admin-api'; // Common Response Wrapper export interface ApiResponse { ok: boolean; data: T; message?: string; } // Data Types export interface FlowStat { id: number; fetched_at: string; flow_id: string; src_ip: string; src_mac: string | null; dst_ip: string; dst_port: number; protocol: string; app_label: string | null; domain: string | null; bytes_download: number; bytes_upload: number; first_seen: string; last_seen: string; } export interface CountryStat { id: number; country_code: string; country_name: string; download: number; upload: number; flow_count: number; } export interface DnsStat { id: number; domain: string; query_count: number; app_label: string | null; category: string | null; } export interface EventStat { id: number; event_type: string; severity: string; message: string; source_ip: string | null; mac_address: string | null; timestamp: string; } export interface TlsVersionStat { id: number; tls_version: string; download: number; upload: number; total: number; } export interface TlsCipherStat { id: number; tls_cipher: string; download: number; upload: number; total: number; } export interface TlsSecurityStat { id: number; tls_security: string; color: string; download: number; upload: number; total: number; } export interface IntelligenceStats { intel_crypto_mining: number; intel_device_discovery: number; intel_encryption_audit: number; intel_insecure_protocols: number; intel_ip_reputation: number; intel_server_discovery: number; intel_tor_detection: number; intel_unencrypted_passwords: number; intel_vpn_detection: number; } export interface DashboardSummary { total_devices: number; total_threats: number; total_events: number; last_fetch: string; bandwidth_down: number; bandwidth_up: number; active_flows: number; download_speed?: number; upload_speed?: number; flow_speed?: number; } export interface TimelinePoint { fetched_at: string; total_download: number; total_upload: number; } export interface AppStat { app_label: string; download: number; upload: number; favicon?: string; category?: string; } export interface ProtocolStat { protocol_label: string; download: number; upload: number; } export interface DeviceStat { id: number; ip_address: string; mac_address: string | null; device_label: string | null; device_type: string | null; os_label: string | null; manufacturer: string | null; download: number; upload: number; last_seen: string; is_gateway_routed?: number | null; } export interface ThreatStat { id: number; threat_type: string | null; severity: string | null; ip_address: string | null; dst_ip: string | null; mac_address: string | null; app_label: string | null; domain: string | null; detected_at: string | null; } export interface AppCategoryStat { id: number; category_label: string; download: number; upload: number; total: number; } export interface ContinentStat { id: number; continent_name: string; download: number; upload: number; total: number; } // Fetcher Function export async function fetcher(endpoint: string): Promise { const url = `/api/dashboard${endpoint}`; // Tambahkan X-View-As-Agent header jika admin dalam mode view-as const viewAsHeaders = getViewAsHeaders(); const res = await fetch(url, { headers: viewAsHeaders }); if (!res.ok) { throw new Error(`API error: ${res.status} ${res.statusText}`); } const json = await res.json(); if (!json.ok) { throw new Error(json.message || 'API responded with ok: false'); } return json.data; } // Custom Hooks for React Components export function useDashboardSummary() { const [data, setData] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher('/summary') .then(setData) .catch(setError) .finally(() => setIsLoading(false)); }, []); return { data, error, isLoading }; } export function useTimeline(points: number = 60) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/timeline?points=${points}`) .then((res) => { // Reverse array if backend sends newest first, so oldest is first for the chart setData(Array.isArray(res) ? res.slice().reverse() : []); }) .catch(setError) .finally(() => setIsLoading(false)); }, [points]); return { data, error, isLoading }; } export function useTopApps(limit: number = 10) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/apps?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useTopProtocols() { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher('/protocols') .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, []); return { data, error, isLoading }; } export function useDevices(limit: number = 50) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/devices?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useThreats(limit: number = 20) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/threats?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useAppCategories() { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher('/app-categories') .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, []); return { data, error, isLoading }; } export function useContinents() { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher('/continents') .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, []); return { data, error, isLoading }; } export function useFlows(limit: number = 50) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/flows?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useCountries(limit: number = 50) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/countries?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useDNS(limit: number = 50) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/dns?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useEvents(limit: number = 50) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/events?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useTlsVersions(limit: number = 10) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/tls-versions?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useTlsCiphers(limit: number = 15) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/tls-ciphers?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useTlsSecurity(limit: number = 10) { const [data, setData] = useState([]); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher(`/tls-security?limit=${limit}`) .then((res) => setData(res || [])) .catch(setError) .finally(() => setIsLoading(false)); }, [limit]); return { data, error, isLoading }; } export function useIntelligenceStats() { const [data, setData] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetcher('/intelligence/stats') .then(setData) .catch(setError) .finally(() => setIsLoading(false)); }, []); return { data, error, isLoading }; } // ─── LOOKUP APPLICATIONS ────────────────────────────────────────────────────── export interface LookupApp { id: number; tag: string; label: string; name: string; full_name: string; description: string; favicon: string; icon: string; logo: string; application_category: { id: number; tag: string; label: string }; } export interface LookupApplicationsResponse { applications: LookupApp[]; pagination: { total_records: number; total_pages: number; current_page: number; start: number; length: number; limit: number; }; } export function useLookupApplications(page: number = 1, limit: number = 25, search: string = '') { const [data, setData] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { setIsLoading(true); fetcher(`/lookup/applications?page=${page}&limit=${limit}&search=${encodeURIComponent(search)}`) .then(setData) .catch(setError) .finally(() => setIsLoading(false)); }, [page, limit, search]); return { data, error, isLoading }; } // ─── INTERACTIVE DETAIL TYPES ────────────────────────────────────────────────── export interface AgentDeviceItem { ip_address: string | null; mac_address: string | null; device_label: string | null; device_type: string | null; os_label: string | null; manufacturer: string | null; last_seen: string | null; download: number; upload: number; encrypted_pct: number | null; risk_level: string | null; has_insecure: boolean; } export interface AgentFlowItem { flow_id?: string | null; src_ip: string | null; dst_ip: string | null; dst_port: number | null; protocol: string | null; app_label?: string | null; domain?: string | null; download: number; upload: number; last_seen: string | null; } export interface AgentAppItem { app_id: number | null; app_label: string; category: string | null; favicon: string | null; download: number; upload: number; } export interface AgentSummary { total_devices: number; active_flows: number; bandwidth_down: number; bandwidth_up: number; } export interface AgentEncryptionItem { ip_address: string | null; mac_address: string | null; device_label: string | null; encrypted_pct: number | null; unencrypted: number | null; encrypted: number | null; total: number | null; risk_level: string | null; detected_at: string | null; } export interface AgentInsecureProtocolItem { ip_address: string | null; mac_address: string | null; protocol: string | null; risk: string | null; app_label: string | null; dst_ip: string | null; dst_port: number | null; download: number; upload: number; detected_at: string | null; } export interface AgentUnencryptedPwdItem { ip_address: string | null; mac_address: string | null; dst_ip: string | null; dst_port: number | null; protocol: string | null; username: string | null; severity: string | null; download: number; upload: number; detected_at: string | null; } export interface AgentIpReputationItem { ip_address: string | null; local_ip: string | null; mac_address: string | null; reputation: string | null; score: number | null; country: string | null; app_label: string | null; blacklisted: number | boolean; download: number; upload: number; detected_at: string | null; } export interface AgentTorItem { ip_address: string | null; mac_address: string | null; exit_node: string | null; circuit_id: string | null; country: string | null; download: number; upload: number; detected_at: string | null; } export interface AgentVpnItem { ip_address: string | null; mac_address: string | null; vpn_type: string | null; remote_ip: string | null; protocol: string | null; country: string | null; confidence: number | null; download: number; upload: number; detected_at: string | null; } export interface AgentSecurity { encryption_audit: AgentEncryptionItem[]; insecure_protocols: AgentInsecureProtocolItem[]; unencrypted_passwords: AgentUnencryptedPwdItem[]; ip_reputation: AgentIpReputationItem[]; tor_detections: AgentTorItem[]; vpn_detections: AgentVpnItem[]; } export interface AgentEventItem { event_id: string | null; event_type: string | null; severity: string | null; ip_address: string | null; mac_address: string | null; description: string | null; event_at: string | null; } export interface AgentMacBandwidthItem { mac_address: string; manufacturer: string | null; download: number; upload: number; total: number; } export interface AgentServerDiscoveryItem { ip_address: string | null; mac_address: string | null; server_type: string | null; hostname: string | null; port: number | null; protocol: string | null; os_label: string | null; download: number; upload: number; detected_at: string | null; } export interface AgentDetails { agent_uuid: string; agent_label: string; summary: AgentSummary | null; devices: AgentDeviceItem[]; flows: AgentFlowItem[]; top_apps: AgentAppItem[]; security: AgentSecurity | null; events: AgentEventItem[]; mac_bandwidth: AgentMacBandwidthItem[]; server_discovery: AgentServerDiscoveryItem[]; } export interface DeviceFlowItem { dst_ip: string | null; dst_port: number | null; protocol: string | null; app_label?: string | null; domain?: string | null; download: number; upload: number; last_seen: string | null; } export interface SecurityInfo { encrypted_pct: number | null; encrypted_bytes: number | null; unencrypted_bytes: number | null; risk_level: string | null; insecure_protocols: { protocol: string; risk: string }[]; device_type: string | null; os_label: string | null; manufacturer: string | null; device_label: string | null; reputation: string | null; rep_score: number | null; blacklisted: boolean; } export interface DeviceInfo { device_label: string | null; device_type: string | null; os_label: string | null; manufacturer: string | null; mac_address: string | null; is_new: number | null; } export interface DeviceAppItem { label: string; // domain name OR protocol name sub_label: string | null; // protocol when label is domain; null otherwise type: 'domain' | 'protocol' | 'port'; download: number; upload: number; flow_count: number; } export interface DeviceDomainItem { domain: string; download: number; upload: number; flow_count: number; } export interface DeviceEncryption { encrypted_pct: number | null; encrypted_bytes: number | null; unencrypted_bytes: number | null; total_bytes: number | null; risk_level: string | null; } export interface DeviceServerItem { server_type: string | null; hostname: string | null; port: number | null; protocol: string | null; os_label: string | null; download: number; upload: number; detected_at: string | null; } export interface DevicePwdItem { dst_ip: string | null; dst_port: number | null; protocol: string | null; username: string | null; severity: string | null; download: number; upload: number; detected_at: string | null; } export interface DeviceReputationItem { remote_ip: string | null; local_ip: string | null; reputation: string | null; score: number | null; country: string | null; app_label: string | null; blacklisted: boolean; download: number; upload: number; } export interface DeviceVpnItem { vpn_type: string | null; remote_ip: string | null; protocol: string | null; country: string | null; confidence: number | null; download: number; upload: number; detected_at: string | null; } export interface DeviceEventItem { event_type: string | null; severity: string | null; ip_address: string | null; mac_address: string | null; description: string | null; event_at: string | null; } export interface DeviceMacBandwidth { mac_address: string; manufacturer: string | null; download: number; upload: number; total: number; } export interface DeviceDetails { ip: string; mac_address: string | null; total_download: number; total_upload: number; flow_count: number; device_info: DeviceInfo; top_apps: DeviceAppItem[]; top_domains: DeviceDomainItem[]; flows: DeviceFlowItem[]; encryption: DeviceEncryption | null; server_discovery: DeviceServerItem[]; unencrypted_passwords: DevicePwdItem[]; ip_reputation: DeviceReputationItem[]; vpn_detections: DeviceVpnItem[]; events: DeviceEventItem[]; mac_bandwidth: DeviceMacBandwidth | null; } export interface AppAgentItem { agent_uuid: string | null; agent_label: string; download: number; upload: number; flow_count?: number; } export interface AppIpItem { ip_address: string | null; download: number; upload: number; flow_count?: number; reputation?: string | null; blacklisted?: boolean; dst_ip?: string | null; domain?: string | null; last_seen?: string | null; } export interface AppDetails { app_label: string; total_download: number; total_upload: number; data_source?: string; has_domain_breakdown?: boolean; agent_scorecard: AppAgentItem[]; top_ips: AppIpItem[]; } export interface SecurityDevice { ip_address: string; mac_address: string | null; device_label: string | null; encrypted_pct: number; unencrypted: number; encrypted: number; total: number; risk_level: 'Rawan' | 'Sedang' | 'Aman'; has_insecure: boolean; device_type: string | null; os_label: string | null; manufacturer: string | null; } // ─── DETAIL FETCH FUNCTIONS (one-shot, no polling) ─────────────────────────── export async function fetchAgentDetails(uuid: string): Promise { return fetcher(`/agent-details?uuid=${encodeURIComponent(uuid)}`); } export async function fetchDeviceDetails(ip: string): Promise { return fetcher(`/device-details?ip=${encodeURIComponent(ip)}`); } export async function fetchAppDetails(label: string): Promise { return fetcher(`/app-details?label=${encodeURIComponent(label)}`); } export async function fetchSecurityDevices(): Promise { return fetcher('/security-devices'); }