169
web/frontend/src/components/FlareSolverrStatus.tsx
Normal file
169
web/frontend/src/components/FlareSolverrStatus.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
import React from 'react'
|
||||
import { api } from '../lib/api'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
|
||||
export const FlareSolverrStatus: React.FC = () => {
|
||||
const { data, isLoading, error, refetch } = useQuery({
|
||||
queryKey: ['flaresolverr-health'],
|
||||
queryFn: () => api.getFlareSolverrHealth(),
|
||||
refetchInterval: 30000, // Check every 30 seconds
|
||||
retry: 1
|
||||
})
|
||||
|
||||
const getStatusColor = (status?: string) => {
|
||||
switch (status) {
|
||||
case 'healthy':
|
||||
return 'bg-green-500'
|
||||
case 'unhealthy':
|
||||
return 'bg-yellow-500'
|
||||
case 'offline':
|
||||
case 'timeout':
|
||||
case 'error':
|
||||
return 'bg-red-500'
|
||||
default:
|
||||
return 'bg-gray-500'
|
||||
}
|
||||
}
|
||||
|
||||
const getStatusText = (status?: string) => {
|
||||
switch (status) {
|
||||
case 'healthy':
|
||||
return 'Healthy'
|
||||
case 'unhealthy':
|
||||
return 'Unhealthy'
|
||||
case 'offline':
|
||||
return 'Offline'
|
||||
case 'timeout':
|
||||
return 'Timeout'
|
||||
case 'error':
|
||||
return 'Error'
|
||||
default:
|
||||
return 'Unknown'
|
||||
}
|
||||
}
|
||||
|
||||
const getStatusIcon = (status?: string) => {
|
||||
switch (status) {
|
||||
case 'healthy':
|
||||
return (
|
||||
<svg className="w-4 h-4 text-green-400" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
)
|
||||
case 'unhealthy':
|
||||
return (
|
||||
<svg className="w-4 h-4 text-yellow-400" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
|
||||
</svg>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<svg className="w-4 h-4 text-red-400" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center space-x-2 text-sm text-slate-400">
|
||||
<div className="w-2 h-2 bg-gray-500 rounded-full animate-pulse"></div>
|
||||
<span>Checking FlareSolverr...</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex items-center space-x-2 text-sm text-red-400">
|
||||
<div className="w-2 h-2 bg-red-500 rounded-full"></div>
|
||||
<span>Failed to check FlareSolverr</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-3">
|
||||
{/* Status Indicator */}
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className={`w-2 h-2 ${getStatusColor(data?.status)} rounded-full ${data?.status === 'healthy' ? 'animate-pulse' : ''}`}></div>
|
||||
<span className="text-sm font-medium text-slate-200">FlareSolverr</span>
|
||||
</div>
|
||||
|
||||
{/* Status Badge */}
|
||||
<div className="flex items-center space-x-1.5 px-2 py-1 rounded-md bg-slate-800/50">
|
||||
{getStatusIcon(data?.status)}
|
||||
<span className="text-xs font-medium text-slate-300">{getStatusText(data?.status)}</span>
|
||||
</div>
|
||||
|
||||
{/* Response Time (if healthy) */}
|
||||
{data?.status === 'healthy' && data.response_time_ms && (
|
||||
<span className="text-xs text-slate-400">
|
||||
{data.response_time_ms}ms
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Sessions Count (if healthy) */}
|
||||
{data?.status === 'healthy' && data.sessions && (
|
||||
<span className="text-xs text-slate-400">
|
||||
{data.sessions.length} session{data.sessions.length !== 1 ? 's' : ''}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Error Message (if not healthy) */}
|
||||
{data?.status !== 'healthy' && data?.error && (
|
||||
<span className="text-xs text-red-400 max-w-xs truncate" title={data.error}>
|
||||
{data.error}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Manual Refresh Button */}
|
||||
<button
|
||||
onClick={() => refetch()}
|
||||
className="p-1 rounded hover:bg-slate-700/50 transition-colors"
|
||||
title="Refresh status"
|
||||
>
|
||||
<svg className="w-4 h-4 text-slate-400 hover:text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Compact version for sidebar or small spaces
|
||||
export const FlareSolverrStatusCompact: React.FC = () => {
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['flaresolverr-health'],
|
||||
queryFn: () => api.getFlareSolverrHealth(),
|
||||
refetchInterval: 30000,
|
||||
retry: 1
|
||||
})
|
||||
|
||||
const getStatusColor = (status?: string) => {
|
||||
switch (status) {
|
||||
case 'healthy':
|
||||
return 'bg-green-500'
|
||||
case 'unhealthy':
|
||||
return 'bg-yellow-500'
|
||||
case 'offline':
|
||||
case 'timeout':
|
||||
case 'error':
|
||||
return 'bg-red-500'
|
||||
default:
|
||||
return 'bg-gray-500'
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <div className="w-2 h-2 bg-gray-500 rounded-full animate-pulse"></div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`w-2 h-2 ${getStatusColor(data?.status)} rounded-full ${data?.status === 'healthy' ? 'animate-pulse' : ''}`}
|
||||
title={`FlareSolverr: ${data?.status || 'unknown'}`}
|
||||
></div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user