diff --git a/frontend/src/pages/Settings/Status.tsx b/frontend/src/pages/Settings/Status.tsx
index db5e511a..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,6 +313,9 @@ export default function Status() {
+ {/* API Monitoring Status Card */}
+
+
{/* Last Updated */}
Last updated: {new Date(status.timestamp).toLocaleString()}
@@ -321,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 (
+
+ );
+ case 'warning':
+ return (
+
+ );
+ case 'critical':
+ return (
+
+ );
+ default:
+ return (
+
+ );
+ }
+ };
+
+ 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
+
+
+
+
+ );
+}