api monitors
This commit is contained in:
270
frontend/src/components/debug/ApiStatusGroupMonitor.tsx
Normal file
270
frontend/src/components/debug/ApiStatusGroupMonitor.tsx
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||||
|
|
||||||
|
export interface ApiEndpoint {
|
||||||
|
name: string;
|
||||||
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
||||||
|
endpoint: string;
|
||||||
|
testData?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiStatus {
|
||||||
|
endpoint: string;
|
||||||
|
status: 'pending' | 'success' | 'error';
|
||||||
|
responseTime?: number;
|
||||||
|
error?: string;
|
||||||
|
lastChecked?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiStatusGroupMonitorProps {
|
||||||
|
groupName: string;
|
||||||
|
endpoints: ApiEndpoint[];
|
||||||
|
baseUrl?: string;
|
||||||
|
onStatusChange?: (status: 'pending' | 'success' | 'error') => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ApiStatusGroupMonitor({
|
||||||
|
groupName,
|
||||||
|
endpoints,
|
||||||
|
baseUrl,
|
||||||
|
onStatusChange
|
||||||
|
}: ApiStatusGroupMonitorProps) {
|
||||||
|
const debugEnabled = useResourceDebug();
|
||||||
|
const [overallStatus, setOverallStatus] = useState<'pending' | 'success' | 'error'>('pending');
|
||||||
|
|
||||||
|
const checkAllApis = useCallback(async () => {
|
||||||
|
if (!debugEnabled) {
|
||||||
|
setOverallStatus('pending');
|
||||||
|
if (onStatusChange) {
|
||||||
|
onStatusChange('pending');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set pending while checking
|
||||||
|
setOverallStatus('pending');
|
||||||
|
if (onStatusChange) {
|
||||||
|
onStatusChange('pending');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const API_BASE_URL = baseUrl || (import.meta.env.VITE_BACKEND_URL || 'https://api.igny8.com/api');
|
||||||
|
const statuses: ApiStatus[] = [];
|
||||||
|
|
||||||
|
// Check all endpoints in parallel with timeout
|
||||||
|
const checkPromises = endpoints.map(async (endpoint) => {
|
||||||
|
try {
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
// Get auth token
|
||||||
|
const authStorage = localStorage.getItem('auth-storage');
|
||||||
|
const token = authStorage ? JSON.parse(authStorage)?.state?.token : null;
|
||||||
|
|
||||||
|
const headers: HeadersInit = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
headers['Authorization'] = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create abort controller for timeout
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
|
||||||
|
|
||||||
|
const options: RequestInit = {
|
||||||
|
method: endpoint.method,
|
||||||
|
headers,
|
||||||
|
credentials: 'include',
|
||||||
|
signal: controller.signal,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only add body for methods that support it
|
||||||
|
if (['POST', 'PUT', 'PATCH'].includes(endpoint.method) && endpoint.testData) {
|
||||||
|
options.body = JSON.stringify(endpoint.testData);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}${endpoint.endpoint}`, options);
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
endpoint: endpoint.endpoint,
|
||||||
|
status: response.ok ? 'success' : 'error',
|
||||||
|
responseTime: Date.now() - startTime,
|
||||||
|
error: response.ok ? undefined : `HTTP ${response.status}`,
|
||||||
|
lastChecked: new Date(),
|
||||||
|
} as ApiStatus;
|
||||||
|
} catch (fetchError: any) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
// Handle timeout and other errors gracefully
|
||||||
|
return {
|
||||||
|
endpoint: endpoint.endpoint,
|
||||||
|
status: 'error' as const,
|
||||||
|
error: fetchError.name === 'AbortError' ? 'Timeout' : (fetchError.message || 'Network error'),
|
||||||
|
lastChecked: new Date(),
|
||||||
|
} as ApiStatus;
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
// Fallback error handling
|
||||||
|
return {
|
||||||
|
endpoint: endpoint.endpoint,
|
||||||
|
status: 'error' as const,
|
||||||
|
error: error.message || 'Check failed',
|
||||||
|
lastChecked: new Date(),
|
||||||
|
} as ApiStatus;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await Promise.allSettled(checkPromises);
|
||||||
|
results.forEach((result) => {
|
||||||
|
if (result.status === 'fulfilled') {
|
||||||
|
statuses.push(result.value);
|
||||||
|
} else {
|
||||||
|
statuses.push({
|
||||||
|
endpoint: 'unknown',
|
||||||
|
status: 'error' as const,
|
||||||
|
error: result.reason?.message || 'Check failed',
|
||||||
|
lastChecked: new Date(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Determine overall status
|
||||||
|
const hasError = statuses.some(s => s.status === 'error');
|
||||||
|
const allSuccess = statuses.length > 0 && statuses.every(s => s.status === 'success');
|
||||||
|
|
||||||
|
let newStatus: 'pending' | 'success' | 'error';
|
||||||
|
if (hasError) {
|
||||||
|
newStatus = 'error';
|
||||||
|
} else if (allSuccess) {
|
||||||
|
newStatus = 'success';
|
||||||
|
} else {
|
||||||
|
newStatus = 'pending';
|
||||||
|
}
|
||||||
|
|
||||||
|
setOverallStatus(newStatus);
|
||||||
|
if (onStatusChange) {
|
||||||
|
onStatusChange(newStatus);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// If something goes wrong, set to error but don't block
|
||||||
|
console.error('API status check error:', error);
|
||||||
|
setOverallStatus('error');
|
||||||
|
if (onStatusChange) {
|
||||||
|
onStatusChange('error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [debugEnabled, endpoints, baseUrl, onStatusChange]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!debugEnabled) {
|
||||||
|
setOverallStatus('pending');
|
||||||
|
if (onStatusChange) {
|
||||||
|
onStatusChange('pending');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial check with delay to avoid blocking page load
|
||||||
|
const initialTimeout = setTimeout(() => {
|
||||||
|
checkAllApis();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// Set up interval to check every 1 minute (60 seconds)
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
checkAllApis();
|
||||||
|
}, 60000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(initialTimeout);
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, [debugEnabled, checkAllApis, onStatusChange]);
|
||||||
|
|
||||||
|
// Don't render anything - this component just tracks status
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sidebar widget component
|
||||||
|
interface ApiStatusSidebarWidgetProps {
|
||||||
|
authStatus: 'pending' | 'success' | 'error';
|
||||||
|
systemStatus: 'pending' | 'success' | 'error';
|
||||||
|
billingStatus: 'pending' | 'success' | 'error';
|
||||||
|
isExpanded?: boolean;
|
||||||
|
isHovered?: boolean;
|
||||||
|
isMobileOpen?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ApiStatusSidebarWidget({
|
||||||
|
authStatus,
|
||||||
|
systemStatus,
|
||||||
|
billingStatus,
|
||||||
|
isExpanded = true,
|
||||||
|
isHovered = false,
|
||||||
|
isMobileOpen = false
|
||||||
|
}: ApiStatusSidebarWidgetProps) {
|
||||||
|
const debugEnabled = useResourceDebug();
|
||||||
|
|
||||||
|
if (!debugEnabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const showLabels = isExpanded || isHovered || isMobileOpen;
|
||||||
|
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'success':
|
||||||
|
return 'bg-green-500 dark:bg-green-400';
|
||||||
|
case 'error':
|
||||||
|
return 'bg-red-500 dark:bg-red-400';
|
||||||
|
case 'pending':
|
||||||
|
return 'bg-yellow-500 dark:bg-yellow-400';
|
||||||
|
default:
|
||||||
|
return 'bg-gray-400 dark:bg-gray-500';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusTitle = (status: string, group: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'success':
|
||||||
|
return `${group} APIs: All healthy`;
|
||||||
|
case 'error':
|
||||||
|
return `${group} APIs: Some errors detected`;
|
||||||
|
case 'pending':
|
||||||
|
return `${group} APIs: Checking...`;
|
||||||
|
default:
|
||||||
|
return `${group} APIs: Unknown status`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`mb-4 px-2 ${!showLabels ? 'flex justify-center' : ''}`}>
|
||||||
|
{showLabels && (
|
||||||
|
<div className="text-xs uppercase text-gray-400 mb-2 px-2">API Status</div>
|
||||||
|
)}
|
||||||
|
<div className={`flex items-center gap-2 px-2 ${!showLabels ? 'justify-center' : ''}`}>
|
||||||
|
<div
|
||||||
|
className={`w-3 h-3 rounded-full ${getStatusColor(authStatus)} transition-colors`}
|
||||||
|
title={getStatusTitle(authStatus, 'Auth')}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={`w-3 h-3 rounded-full ${getStatusColor(systemStatus)} transition-colors`}
|
||||||
|
title={getStatusTitle(systemStatus, 'System')}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={`w-3 h-3 rounded-full ${getStatusColor(billingStatus)} transition-colors`}
|
||||||
|
title={getStatusTitle(billingStatus, 'Billing')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{showLabels && (
|
||||||
|
<div className="flex items-center gap-2 mt-1 px-2 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
<span className="text-[10px]">A</span>
|
||||||
|
<span className="text-[10px]">S</span>
|
||||||
|
<span className="text-[10px]">B</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
189
frontend/src/components/debug/ApiStatusMonitor.tsx
Normal file
189
frontend/src/components/debug/ApiStatusMonitor.tsx
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||||
|
import { Card } from '../ui/card';
|
||||||
|
import Badge from '../ui/badge/Badge';
|
||||||
|
|
||||||
|
export interface ApiEndpoint {
|
||||||
|
name: string;
|
||||||
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
||||||
|
endpoint: string;
|
||||||
|
testData?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiStatusMonitorProps {
|
||||||
|
title: string;
|
||||||
|
endpoints: ApiEndpoint[];
|
||||||
|
baseUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiStatus {
|
||||||
|
endpoint: string;
|
||||||
|
status: 'pending' | 'success' | 'error';
|
||||||
|
responseTime?: number;
|
||||||
|
error?: string;
|
||||||
|
lastChecked?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ApiStatusMonitor({ title, endpoints, baseUrl }: ApiStatusMonitorProps) {
|
||||||
|
const debugEnabled = useResourceDebug();
|
||||||
|
const [statuses, setStatuses] = useState<Record<string, ApiStatus>>({});
|
||||||
|
const [isChecking, setIsChecking] = useState(false);
|
||||||
|
|
||||||
|
// Only make requests if debug is enabled
|
||||||
|
useEffect(() => {
|
||||||
|
if (!debugEnabled) {
|
||||||
|
// Clear statuses when debug is disabled
|
||||||
|
setStatuses({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial check
|
||||||
|
checkAllApis();
|
||||||
|
|
||||||
|
// Set up interval to check every 30 seconds
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
checkAllApis();
|
||||||
|
}, 30000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [debugEnabled, endpoints]);
|
||||||
|
|
||||||
|
const checkAllApis = async () => {
|
||||||
|
if (!debugEnabled) return;
|
||||||
|
|
||||||
|
setIsChecking(true);
|
||||||
|
const API_BASE_URL = baseUrl || (import.meta.env.VITE_BACKEND_URL || 'https://api.igny8.com/api');
|
||||||
|
|
||||||
|
const newStatuses: Record<string, ApiStatus> = {};
|
||||||
|
|
||||||
|
for (const endpoint of endpoints) {
|
||||||
|
const key = `${endpoint.method}:${endpoint.endpoint}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
// Get auth token
|
||||||
|
const authStorage = localStorage.getItem('auth-storage');
|
||||||
|
const token = authStorage ? JSON.parse(authStorage)?.state?.token : null;
|
||||||
|
|
||||||
|
const headers: HeadersInit = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
headers['Authorization'] = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: RequestInit = {
|
||||||
|
method: endpoint.method,
|
||||||
|
headers,
|
||||||
|
credentials: 'include',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only add body for methods that support it
|
||||||
|
if (['POST', 'PUT', 'PATCH'].includes(endpoint.method) && endpoint.testData) {
|
||||||
|
options.body = JSON.stringify(endpoint.testData);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${API_BASE_URL}${endpoint.endpoint}`, options);
|
||||||
|
const responseTime = Date.now() - startTime;
|
||||||
|
|
||||||
|
newStatuses[key] = {
|
||||||
|
endpoint: endpoint.endpoint,
|
||||||
|
status: response.ok ? 'success' : 'error',
|
||||||
|
responseTime,
|
||||||
|
error: response.ok ? undefined : `HTTP ${response.status}`,
|
||||||
|
lastChecked: new Date(),
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
newStatuses[key] = {
|
||||||
|
endpoint: endpoint.endpoint,
|
||||||
|
status: 'error',
|
||||||
|
error: error.message || 'Network error',
|
||||||
|
lastChecked: new Date(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatuses(newStatuses);
|
||||||
|
setIsChecking(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't render anything if debug is disabled
|
||||||
|
if (!debugEnabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'success':
|
||||||
|
return 'success';
|
||||||
|
case 'error':
|
||||||
|
return 'error';
|
||||||
|
case 'pending':
|
||||||
|
return 'warning';
|
||||||
|
default:
|
||||||
|
return 'primary';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="mt-6 p-4 border-2 border-dashed border-gray-300 dark:border-gray-700">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h3 className="text-sm font-semibold text-gray-900 dark:text-white">
|
||||||
|
{title} API Status Monitor
|
||||||
|
</h3>
|
||||||
|
<Badge variant="light" color={isChecking ? 'warning' : 'success'}>
|
||||||
|
{isChecking ? 'Checking...' : 'Active'}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
{endpoints.map((endpoint) => {
|
||||||
|
const key = `${endpoint.method}:${endpoint.endpoint}`;
|
||||||
|
const status = statuses[key] || { status: 'pending' as const };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={key}
|
||||||
|
className="flex items-center justify-between p-2 rounded-lg bg-gray-50 dark:bg-gray-800/50"
|
||||||
|
>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Badge variant="light" color="primary" className="text-xs">
|
||||||
|
{endpoint.method}
|
||||||
|
</Badge>
|
||||||
|
<span className="text-xs font-mono text-gray-700 dark:text-gray-300 truncate">
|
||||||
|
{endpoint.endpoint}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{status.responseTime && (
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||||
|
{status.responseTime}ms
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{status.error && (
|
||||||
|
<div className="text-xs text-red-600 dark:text-red-400 mt-1">
|
||||||
|
{status.error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="ml-4">
|
||||||
|
<Badge variant="light" color={getStatusColor(status.status)}>
|
||||||
|
{status.status === 'pending' ? '⏳' : status.status === 'success' ? '✓' : '✗'}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{Object.keys(statuses).length > 0 && (
|
||||||
|
<div className="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
Last checked: {statuses[Object.keys(statuses)[0]]?.lastChecked?.toLocaleTimeString()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -20,6 +20,7 @@ import { useSidebar } from "../context/SidebarContext";
|
|||||||
import SidebarWidget from "./SidebarWidget";
|
import SidebarWidget from "./SidebarWidget";
|
||||||
import { APP_VERSION } from "../config/version";
|
import { APP_VERSION } from "../config/version";
|
||||||
import { useAuthStore } from "../store/authStore";
|
import { useAuthStore } from "../store/authStore";
|
||||||
|
import { ApiStatusSidebarWidget, ApiStatusGroupMonitor } from "../components/debug/ApiStatusGroupMonitor";
|
||||||
|
|
||||||
type NavItem = {
|
type NavItem = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -52,6 +53,11 @@ const AppSidebar: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const subMenuRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
const subMenuRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
||||||
|
|
||||||
|
// API Status states for groups
|
||||||
|
const [authStatus, setAuthStatus] = useState<'pending' | 'success' | 'error'>('pending');
|
||||||
|
const [systemStatus, setSystemStatus] = useState<'pending' | 'success' | 'error'>('pending');
|
||||||
|
const [billingStatus, setBillingStatus] = useState<'pending' | 'success' | 'error'>('pending');
|
||||||
|
|
||||||
const isActive = useCallback(
|
const isActive = useCallback(
|
||||||
(path: string) => location.pathname === path,
|
(path: string) => location.pathname === path,
|
||||||
@@ -494,8 +500,52 @@ const AppSidebar: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col overflow-y-auto duration-300 ease-linear no-scrollbar">
|
<div className="flex flex-col overflow-y-auto duration-300 ease-linear no-scrollbar">
|
||||||
|
{/* API Status Monitors - Hidden components that track status */}
|
||||||
|
<ApiStatusGroupMonitor
|
||||||
|
groupName="Auth"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'List Sites', method: 'GET', endpoint: '/v1/auth/sites/' },
|
||||||
|
{ name: 'Get Industries', method: 'GET', endpoint: '/v1/auth/industries/' },
|
||||||
|
{ name: 'Get Seed Keywords', method: 'GET', endpoint: '/v1/auth/seed-keywords/' },
|
||||||
|
]}
|
||||||
|
onStatusChange={setAuthStatus}
|
||||||
|
/>
|
||||||
|
<ApiStatusGroupMonitor
|
||||||
|
groupName="System"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get Integration Settings', method: 'GET', endpoint: '/v1/system/settings/integrations/openai/' },
|
||||||
|
{ name: 'Get Image Generation Settings', method: 'GET', endpoint: '/v1/system/integrations/image_generation/' },
|
||||||
|
{ name: 'Get System Status', method: 'GET', endpoint: '/v1/system/status/' },
|
||||||
|
{ name: 'List Prompts', method: 'GET', endpoint: '/v1/system/prompts/' },
|
||||||
|
{ name: 'List Author Profiles', method: 'GET', endpoint: '/v1/system/author-profiles/' },
|
||||||
|
{ name: 'Get System Settings', method: 'GET', endpoint: '/v1/system/settings/system/' },
|
||||||
|
{ name: 'Get Account Settings', method: 'GET', endpoint: '/v1/system/settings/account/' },
|
||||||
|
{ name: 'Get AI Settings', method: 'GET', endpoint: '/v1/system/settings/ai/' },
|
||||||
|
]}
|
||||||
|
onStatusChange={setSystemStatus}
|
||||||
|
/>
|
||||||
|
<ApiStatusGroupMonitor
|
||||||
|
groupName="Billing"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get Credit Balance', method: 'GET', endpoint: '/v1/billing/credits/balance/balance/' },
|
||||||
|
{ name: 'Get Credit Transactions', method: 'GET', endpoint: '/v1/billing/credits/transactions/' },
|
||||||
|
{ name: 'Get Credit Usage', method: 'GET', endpoint: '/v1/billing/credits/usage/' },
|
||||||
|
{ name: 'Get Usage Limits', method: 'GET', endpoint: '/v1/billing/credits/usage/limits/' },
|
||||||
|
]}
|
||||||
|
onStatusChange={setBillingStatus}
|
||||||
|
/>
|
||||||
|
|
||||||
<nav className="mb-6">
|
<nav className="mb-6">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
{/* API Status Widget - Above OVERVIEW section */}
|
||||||
|
<ApiStatusSidebarWidget
|
||||||
|
authStatus={authStatus}
|
||||||
|
systemStatus={systemStatus}
|
||||||
|
billingStatus={billingStatus}
|
||||||
|
isExpanded={isExpanded}
|
||||||
|
isHovered={isHovered}
|
||||||
|
isMobileOpen={isMobileOpen}
|
||||||
|
/>
|
||||||
{allSections.map((section, sectionIndex) => (
|
{allSections.map((section, sectionIndex) => (
|
||||||
<div key={section.label}>
|
<div key={section.label}>
|
||||||
<h2
|
<h2
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useToast } from '../../components/ui/toast/ToastContainer';
|
|||||||
import { fetchCreditBalance, CreditBalance } from '../../services/api';
|
import { fetchCreditBalance, CreditBalance } from '../../services/api';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
import Badge from '../../components/ui/badge/Badge';
|
import Badge from '../../components/ui/badge/Badge';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
export default function Credits() {
|
export default function Credits() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -88,6 +89,14 @@ export default function Credits() {
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="Billing"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get Credit Balance', method: 'GET', endpoint: '/v1/billing/credits/balance/balance/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useToast } from '../../components/ui/toast/ToastContainer';
|
|||||||
import { fetchCreditTransactions, CreditTransaction } from '../../services/api';
|
import { fetchCreditTransactions, CreditTransaction } from '../../services/api';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
import Badge from '../../components/ui/badge/Badge';
|
import Badge from '../../components/ui/badge/Badge';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
export default function Transactions() {
|
export default function Transactions() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -97,6 +98,14 @@ export default function Transactions() {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="Billing"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get Credit Transactions', method: 'GET', endpoint: '/v1/billing/credits/transactions/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useToast } from '../../components/ui/toast/ToastContainer';
|
|||||||
import { fetchCreditUsage, CreditUsageLog, fetchUsageLimits, LimitCard } from '../../services/api';
|
import { fetchCreditUsage, CreditUsageLog, fetchUsageLimits, LimitCard } from '../../services/api';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
import Badge from '../../components/ui/badge/Badge';
|
import Badge from '../../components/ui/badge/Badge';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
export default function Usage() {
|
export default function Usage() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -211,6 +212,15 @@ export default function Usage() {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="Billing"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get Credit Usage', method: 'GET', endpoint: '/v1/billing/credits/usage/' },
|
||||||
|
{ name: 'Get Usage Limits', method: 'GET', endpoint: '/v1/billing/credits/usage/limits/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import PageMeta from '../../components/common/PageMeta';
|
|||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { fetchAPI } from '../../services/api';
|
import { fetchAPI } from '../../services/api';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
export default function AISettings() {
|
export default function AISettings() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -42,6 +43,14 @@ export default function AISettings() {
|
|||||||
<p className="text-gray-600 dark:text-gray-400">AI settings management interface coming soon.</p>
|
<p className="text-gray-600 dark:text-gray-400">AI settings management interface coming soon.</p>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="System"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get AI Settings', method: 'GET', endpoint: '/v1/system/settings/ai/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import PageMeta from '../../components/common/PageMeta';
|
|||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { fetchAPI } from '../../services/api';
|
import { fetchAPI } from '../../services/api';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
export default function AccountSettings() {
|
export default function AccountSettings() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -42,6 +43,14 @@ export default function AccountSettings() {
|
|||||||
<p className="text-gray-600 dark:text-gray-400">Account settings management interface coming soon.</p>
|
<p className="text-gray-600 dark:text-gray-400">Account settings management interface coming soon.</p>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="System"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get Account Settings', method: 'GET', endpoint: '/v1/system/settings/account/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import SelectDropdown from '../../components/form/SelectDropdown';
|
|||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import Alert from '../../components/ui/alert/Alert';
|
import Alert from '../../components/ui/alert/Alert';
|
||||||
import { fetchAPI } from '../../services/api';
|
import { fetchAPI } from '../../services/api';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
// OpenAI Icon SVG
|
// OpenAI Icon SVG
|
||||||
const OpenAIIcon = () => (
|
const OpenAIIcon = () => (
|
||||||
@@ -1165,6 +1166,16 @@ export default function Integration() {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="System"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get Integration Settings', method: 'GET', endpoint: '/v1/system/settings/integrations/openai/' },
|
||||||
|
{ name: 'Get Image Generation Settings', method: 'GET', endpoint: '/v1/system/integrations/image_generation/' },
|
||||||
|
{ name: 'Get System Status', method: 'GET', endpoint: '/v1/system/status/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
Sector,
|
Sector,
|
||||||
} from '../../services/api';
|
} from '../../services/api';
|
||||||
import Badge from '../../components/ui/badge/Badge';
|
import Badge from '../../components/ui/badge/Badge';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
// Site Icon SVG
|
// Site Icon SVG
|
||||||
const SiteIcon = () => (
|
const SiteIcon = () => (
|
||||||
@@ -591,6 +592,16 @@ export default function Sites() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="Auth"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'List Sites', method: 'GET', endpoint: '/v1/auth/sites/' },
|
||||||
|
{ name: 'Get Industries', method: 'GET', endpoint: '/v1/auth/industries/' },
|
||||||
|
{ name: 'Get Seed Keywords', method: 'GET', endpoint: '/v1/auth/seed-keywords/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from "react";
|
|||||||
import PageMeta from "../../components/common/PageMeta";
|
import PageMeta from "../../components/common/PageMeta";
|
||||||
import ComponentCard from "../../components/common/ComponentCard";
|
import ComponentCard from "../../components/common/ComponentCard";
|
||||||
import { fetchAPI } from "../../services/api";
|
import { fetchAPI } from "../../services/api";
|
||||||
|
import ApiStatusMonitor from "../../components/debug/ApiStatusMonitor";
|
||||||
|
|
||||||
interface SystemStatus {
|
interface SystemStatus {
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
@@ -318,6 +319,14 @@ export default function Status() {
|
|||||||
Last updated: {new Date(status.timestamp).toLocaleString()}
|
Last updated: {new Date(status.timestamp).toLocaleString()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="System"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get System Status', method: 'GET', endpoint: '/v1/system/status/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import PageMeta from '../../components/common/PageMeta';
|
|||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { fetchAPI } from '../../services/api';
|
import { fetchAPI } from '../../services/api';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
export default function SystemSettings() {
|
export default function SystemSettings() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -42,6 +43,14 @@ export default function SystemSettings() {
|
|||||||
<p className="text-gray-600 dark:text-gray-400">System settings management interface coming soon.</p>
|
<p className="text-gray-600 dark:text-gray-400">System settings management interface coming soon.</p>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="System"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'Get System Settings', method: 'GET', endpoint: '/v1/system/settings/system/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Button from '../../components/ui/button/Button';
|
|||||||
import FormModal, { FormField } from '../../components/common/FormModal';
|
import FormModal, { FormField } from '../../components/common/FormModal';
|
||||||
import Badge from '../../components/ui/badge/Badge';
|
import Badge from '../../components/ui/badge/Badge';
|
||||||
import { PlusIcon } from '../../icons';
|
import { PlusIcon } from '../../icons';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
export default function AuthorProfiles() {
|
export default function AuthorProfiles() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -158,6 +159,14 @@ export default function AuthorProfiles() {
|
|||||||
data={formData}
|
data={formData}
|
||||||
onChange={setFormData}
|
onChange={setFormData}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="System"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'List Author Profiles', method: 'GET', endpoint: '/v1/system/author-profiles/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import TextArea from '../../components/form/input/TextArea';
|
|||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { BoltIcon } from '../../icons';
|
import { BoltIcon } from '../../icons';
|
||||||
import { fetchAPI } from '../../services/api';
|
import { fetchAPI } from '../../services/api';
|
||||||
|
import ApiStatusMonitor from '../../components/debug/ApiStatusMonitor';
|
||||||
|
|
||||||
interface PromptData {
|
interface PromptData {
|
||||||
prompt_type: string;
|
prompt_type: string;
|
||||||
@@ -427,6 +428,14 @@ export default function Prompts() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* API Status Monitor - Only shows when debug toggle is enabled */}
|
||||||
|
<ApiStatusMonitor
|
||||||
|
title="System"
|
||||||
|
endpoints={[
|
||||||
|
{ name: 'List Prompts', method: 'GET', endpoint: '/v1/system/prompts/' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user