import { useState, useEffect, useCallback } from "react"; import PageMeta from "../../components/common/PageMeta"; import ComponentCard from "../../components/common/ComponentCard"; import { API_BASE_URL } from "../../services/api"; interface EndpointStatus { endpoint: string; method: string; status: 'healthy' | 'warning' | 'error' | 'checking'; responseTime?: number; lastChecked?: string; error?: string; } interface EndpointGroup { name: string; endpoints: { path: string; method: string; description: string; }[]; } const endpointGroups: EndpointGroup[] = [ { name: "Core Health & Auth", endpoints: [ { path: "/api/ping/", method: "GET", description: "Health check" }, { path: "/v1/system/status/", method: "GET", description: "System status" }, { path: "/v1/auth/login/", method: "POST", description: "Login" }, { path: "/v1/auth/me/", method: "GET", description: "Current user" }, { path: "/v1/auth/register/", method: "POST", description: "Registration" }, ], }, { name: "Auth & User Management", endpoints: [ { path: "/v1/auth/users/", method: "GET", description: "List users" }, { path: "/v1/auth/accounts/", method: "GET", description: "List accounts" }, { path: "/v1/auth/sites/", method: "GET", description: "List sites" }, { path: "/v1/auth/sectors/", method: "GET", description: "List sectors" }, { path: "/v1/auth/plans/", method: "GET", description: "List plans" }, { path: "/v1/auth/industries/", method: "GET", description: "List industries" }, ], }, { name: "Planner Module", endpoints: [ { path: "/v1/planner/keywords/", method: "GET", description: "List keywords" }, { path: "/v1/planner/keywords/auto_cluster/", method: "POST", description: "AI clustering" }, { path: "/v1/planner/clusters/", method: "GET", description: "List clusters" }, { path: "/v1/planner/clusters/auto_generate_ideas/", method: "POST", description: "AI ideas" }, { path: "/v1/planner/ideas/", method: "GET", description: "List ideas" }, ], }, { name: "Writer Module", endpoints: [ { path: "/v1/writer/tasks/", method: "GET", description: "List tasks" }, { path: "/v1/writer/tasks/auto_generate_content/", method: "POST", description: "AI content" }, { path: "/v1/writer/content/", method: "GET", description: "List content" }, { path: "/v1/writer/content/generate_image_prompts/", method: "POST", description: "Image prompts" }, { path: "/v1/writer/images/", method: "GET", description: "List images" }, { path: "/v1/writer/images/generate_images/", method: "POST", description: "AI images" }, ], }, { name: "System & Billing", endpoints: [ { path: "/v1/system/prompts/", method: "GET", description: "List prompts" }, { path: "/v1/system/settings/integrations/1/test/", method: "POST", description: "Test integration" }, { path: "/v1/billing/credits/balance/balance/", method: "GET", description: "Credit balance" }, { path: "/v1/billing/credits/usage/", method: "GET", description: "Usage logs" }, ], }, ]; const getStatusColor = (status: string) => { switch (status) { case 'healthy': return 'text-green-600 dark:text-green-400'; case 'warning': return 'text-yellow-600 dark:text-yellow-400'; case 'error': return 'text-red-600 dark:text-red-400'; case 'checking': return 'text-blue-600 dark:text-blue-400'; default: return 'text-gray-600 dark:text-gray-400'; } }; const getStatusBadge = (status: string) => { switch (status) { case 'healthy': return 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'; case 'warning': return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400'; case 'error': return 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'; case 'checking': return 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400'; default: return 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400'; } }; const getStatusIcon = (status: string) => { switch (status) { case 'healthy': return '✓'; case 'warning': return '⚠'; case 'error': return '✗'; case 'checking': return '⟳'; default: return '?'; } }; export default function ApiMonitor() { const [endpointStatuses, setEndpointStatuses] = useState>({}); const [loading, setLoading] = useState(false); const [autoRefresh, setAutoRefresh] = useState(true); const [refreshInterval, setRefreshInterval] = useState(30); // seconds const checkEndpoint = useCallback(async (path: string, method: string) => { const key = `${method}:${path}`; // Set checking status setEndpointStatuses(prev => ({ ...prev, [key]: { endpoint: path, method, status: 'checking', }, })); const startTime = Date.now(); try { // Use fetch directly for monitoring to get response status // Get token from auth store or localStorage const token = localStorage.getItem('auth_token') || (() => { try { const authStorage = localStorage.getItem('auth-storage'); if (authStorage) { const parsed = JSON.parse(authStorage); return parsed?.state?.token || ''; } } catch (e) { // Ignore parsing errors } return ''; })(); const headers: HeadersInit = { 'Content-Type': 'application/json', }; if (token) { headers['Authorization'] = `Bearer ${token}`; } const fetchOptions: RequestInit = { method, headers, credentials: 'include', }; // For POST endpoints, send empty body for monitoring if (method === 'POST') { fetchOptions.body = JSON.stringify({}); } const response = await fetch(`${API_BASE_URL}${path}`, fetchOptions); const responseTime = Date.now() - startTime; // Check response status // 2xx = healthy, 4xx = warning (endpoint exists but auth/validation issue), 5xx = error let status: 'healthy' | 'warning' | 'error' = 'healthy'; if (response.status >= 500) { status = 'error'; } else if (response.status >= 400 && response.status < 500) { status = 'warning'; // 4xx is warning (auth/permission/validation - endpoint exists) } setEndpointStatuses(prev => ({ ...prev, [key]: { endpoint: path, method, status, responseTime, lastChecked: new Date().toISOString(), }, })); } catch (err: any) { const responseTime = Date.now() - startTime; // Network errors or timeouts are real errors setEndpointStatuses(prev => ({ ...prev, [key]: { endpoint: path, method, status: 'error', responseTime, lastChecked: new Date().toISOString(), error: err instanceof Error ? err.message : 'Network error', }, })); } }, []); const checkAllEndpoints = useCallback(async () => { setLoading(true); // Check all endpoints in parallel (but limit concurrency) const allChecks = endpointGroups.flatMap(group => group.endpoints.map(ep => checkEndpoint(ep.path, ep.method)) ); // Check in batches of 5 to avoid overwhelming the server const batchSize = 5; for (let i = 0; i < allChecks.length; i += batchSize) { const batch = allChecks.slice(i, i + batchSize); await Promise.all(batch); // Small delay between batches if (i + batchSize < allChecks.length) { await new Promise(resolve => setTimeout(resolve, 100)); } } setLoading(false); }, [checkEndpoint]); useEffect(() => { // Only check when component is mounted (page is visible) checkAllEndpoints(); // Set up auto-refresh if enabled if (autoRefresh) { const interval = setInterval(() => { checkAllEndpoints(); }, refreshInterval * 1000); return () => clearInterval(interval); } }, [autoRefresh, refreshInterval, checkAllEndpoints]); const getEndpointStatus = (path: string, method: string): EndpointStatus => { const key = `${method}:${path}`; return endpointStatuses[key] || { endpoint: path, method, status: 'checking', }; }; const getGroupHealth = (group: EndpointGroup) => { const statuses = group.endpoints.map(ep => getEndpointStatus(ep.path, ep.method).status); const healthy = statuses.filter(s => s === 'healthy').length; const total = statuses.length; return { healthy, total }; }; return ( <>
{/* Header Controls */}

API Monitor

Monitor API endpoint health and response times

{autoRefresh && ( )}
{/* Monitoring Tables - 3 per row */}
{endpointGroups.map((group, groupIndex) => { const groupHealth = getGroupHealth(group); return (
{group.endpoints.map((endpoint, epIndex) => { const status = getEndpointStatus(endpoint.path, endpoint.method); return ( ); })}
Endpoint Status Time
{endpoint.method}
{endpoint.path}
{endpoint.description}
{getStatusIcon(status.status)} {status.status} {status.responseTime ? ( {status.responseTime}ms ) : ( - )}
); })}
{/* Summary Stats */}
{endpointGroups.map((group, index) => { const groupHealth = getGroupHealth(group); const percentage = groupHealth.total > 0 ? Math.round((groupHealth.healthy / groupHealth.total) * 100) : 0; return (
{percentage}%
{group.name}
{groupHealth.healthy}/{groupHealth.total} healthy
); })}
); }