diff --git a/frontend/src/pages/Settings/Status.tsx b/frontend/src/pages/Settings/Status.tsx index 145b1a09..ad658edb 100644 --- a/frontend/src/pages/Settings/Status.tsx +++ b/frontend/src/pages/Settings/Status.tsx @@ -1,7 +1,7 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import PageMeta from "../../components/common/PageMeta"; import ComponentCard from "../../components/common/ComponentCard"; -import { fetchAPI } from "../../services/api"; +import { fetchAPI, API_BASE_URL } from "../../services/api"; interface SystemStatus { timestamp: string; @@ -313,46 +313,8 @@ export default function Status() { - {/* API Status Card */} - -
-
-
-
- API Server - - {status.database?.connected ? 'Operational' : 'Offline'} - -
-
- Base URL: {typeof window !== 'undefined' ? window.location.origin.replace('app.', 'api.') : 'api.igny8.com'} -
-
- -
-
- Response Format - - Unified - -
-
- All endpoints use standardized response format -
-
-
- -
-
-
• Health Check: /api/ping/
-
• System Status: /v1/system/status/
-
• Authentication: /v1/auth/
-
• Planner API: /v1/planner/
-
• Writer API: /v1/writer/
-
-
-
-
+ {/* API Monitoring Status Card */} + {/* Last Updated */}
@@ -362,3 +324,229 @@ export default function Status() { ); } + +// API Monitoring Component +interface APIEndpointStatus { + name: string; + endpoint: string; + status: 'healthy' | 'warning' | 'critical' | 'checking'; + responseTime: number | null; + statusCode: number | null; + lastChecked: Date | null; + error: string | null; +} + +function APIMonitoringCard() { + const initialEndpoints: APIEndpointStatus[] = [ + { name: 'Health Check', endpoint: '/api/ping/', status: 'checking', responseTime: null, statusCode: null, lastChecked: null, error: null }, + { name: 'System Status', endpoint: '/v1/system/status/', status: 'checking', responseTime: null, statusCode: null, lastChecked: null, error: null }, + { name: 'Auth Endpoint', endpoint: '/v1/auth/me/', status: 'checking', responseTime: null, statusCode: null, lastChecked: null, error: null }, + { name: 'Planner API', endpoint: '/v1/planner/keywords/?page=1&page_size=1', status: 'checking', responseTime: null, statusCode: null, lastChecked: null, error: null }, + { name: 'Writer API', endpoint: '/v1/writer/tasks/?page=1&page_size=1', status: 'checking', responseTime: null, statusCode: null, lastChecked: null, error: null }, + ]; + + const [endpoints, setEndpoints] = useState(initialEndpoints); + const endpointsRef = useRef(initialEndpoints); + + // Keep ref in sync with state + useEffect(() => { + endpointsRef.current = endpoints; + }, [endpoints]); + + const checkEndpoint = async (endpoint: APIEndpointStatus) => { + const startTime = performance.now(); + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout + + const response = await fetch(`${API_BASE_URL}${endpoint.endpoint}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + signal: controller.signal, + }); + + clearTimeout(timeoutId); + const endTime = performance.now(); + const responseTime = Math.round(endTime - startTime); + + // Determine status based on response time and status code + let status: 'healthy' | 'warning' | 'critical' = 'healthy'; + if (response.status >= 500) { + status = 'critical'; + } else if (response.status >= 400 || responseTime > 2000) { + status = 'warning'; + } else if (responseTime > 1000) { + status = 'warning'; + } + + return { + ...endpoint, + status, + responseTime, + statusCode: response.status, + lastChecked: new Date(), + error: null, + }; + } catch (err: any) { + const endTime = performance.now(); + const responseTime = Math.round(endTime - startTime); + + return { + ...endpoint, + status: 'critical' as const, + responseTime, + statusCode: null, + lastChecked: new Date(), + error: err.name === 'AbortError' ? 'Timeout' : err.message || 'Network Error', + }; + } + }; + + useEffect(() => { + const checkAllEndpoints = async () => { + // Check all endpoints in parallel using ref to get latest state + const results = await Promise.all( + endpointsRef.current.map(endpoint => checkEndpoint(endpoint)) + ); + setEndpoints(results); + }; + + // Initial check + checkAllEndpoints(); + + // Check every 5 seconds for real-time monitoring + const interval = setInterval(checkAllEndpoints, 5000); + + return () => clearInterval(interval); + }, []); + + const getStatusIcon = (status: string) => { + switch (status) { + case 'healthy': + return ( +
+
+ Online +
+ ); + case 'warning': + return ( +
+
+ Slow +
+ ); + case 'critical': + return ( +
+
+ Down +
+ ); + default: + return ( +
+
+ Checking... +
+ ); + } + }; + + const getResponseTimeColor = (responseTime: number | null) => { + if (responseTime === null) return 'text-gray-500 dark:text-gray-400'; + if (responseTime < 500) return 'text-green-600 dark:text-green-400'; + if (responseTime < 1000) return 'text-yellow-600 dark:text-yellow-400'; + if (responseTime < 2000) return 'text-orange-600 dark:text-orange-400'; + return 'text-red-600 dark:text-red-400'; + }; + + const overallStatus = endpoints.every(e => e.status === 'healthy') + ? 'healthy' + : endpoints.some(e => e.status === 'critical') + ? 'critical' + : 'warning'; + + return ( + +
+ {/* Overall Status */} +
+
+

Overall API Status

+

+ {endpoints.filter(e => e.status === 'healthy').length} of {endpoints.length} endpoints healthy +

+
+
+ {overallStatus === 'healthy' ? '✓ All Systems Operational' : + overallStatus === 'warning' ? '⚠ Some Issues Detected' : + '✗ Critical Issues'} +
+
+ + {/* Endpoints List */} +
+ {endpoints.map((endpoint, index) => ( +
+
+
+ + {endpoint.name} + + {getStatusIcon(endpoint.status)} +
+
+ {endpoint.endpoint} + {endpoint.responseTime !== null && ( + + {endpoint.responseTime}ms + + )} + {endpoint.statusCode && ( + = 200 && endpoint.statusCode < 300 + ? 'text-green-600 dark:text-green-400' + : endpoint.statusCode >= 400 && endpoint.statusCode < 500 + ? 'text-yellow-600 dark:text-yellow-400' + : 'text-red-600 dark:text-red-400' + }`}> + {endpoint.statusCode} + + )} +
+ {endpoint.error && ( +
+ {endpoint.error} +
+ )} + {endpoint.lastChecked && ( +
+ Last checked: {endpoint.lastChecked.toLocaleTimeString()} +
+ )} +
+
+ ))} +
+ + {/* Refresh Indicator */} +
+ + + Auto-refreshing every 5 seconds + +
+
+
+ ); +}