| // src/components/admin/AdminSystemHealth.tsx | |
| import { Server, Database, Activity, CheckCircle, AlertCircle, Cpu, RefreshCw } from "lucide-react"; | |
| import { useState, useEffect } from "react"; | |
| import api from "@/lib/api"; | |
| import { motion } from "framer-motion"; | |
| export function AdminSystemHealth() { | |
| const [health, setHealth] = useState<any>(null); | |
| const [loading, setLoading] = useState(true); | |
| const [error, setError] = useState(false); | |
| const fetchHealth = async () => { | |
| setLoading(true); | |
| try { | |
| const res = await api.get("/health/details"); | |
| setHealth(res.data); | |
| setError(false); | |
| } catch (err) { | |
| console.error("Failed to fetch detailed health status, trying basic health status", err); | |
| try { | |
| const basicRes = await api.get("/health"); | |
| setHealth({ basic: true, ...basicRes.data }); | |
| setError(false); | |
| } catch (basicErr) { | |
| console.error("Failed to fetch health status", basicErr); | |
| setError(true); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| useEffect(() => { | |
| fetchHealth(); | |
| const interval = setInterval(fetchHealth, 15000); | |
| return () => clearInterval(interval); | |
| }, []); | |
| if (loading && !health) { | |
| return ( | |
| <div className="bg-white/60 dark:bg-gray-900/60 backdrop-blur-md border border-gray-200 dark:border-gray-800 rounded-2xl p-6 shadow-xl animate-pulse min-h-[300px] flex items-center justify-center"> | |
| <div className="text-center space-y-2"> | |
| <Activity className="w-8 h-8 text-primary animate-pulse mx-auto" /> | |
| <p className="text-gray-500 text-sm">Monitoring system metrics...</p> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| const isDbUp = health?.details?.database?.error ? false : (health?.checks?.database?.status === "UP" || (health?.details?.database && !health?.details?.database?.error)); | |
| const isDiskUp = health?.checks?.diskSpace?.status === "UP" || health?.checks?.diskSpace; | |
| const isOverallUp = health?.overall === "UP" || health?.status === "UP" || health?.status === "ready"; | |
| const system = health?.details?.system || {}; | |
| const jvm = health?.details?.jvm || {}; | |
| const uptimeStr = health?.details?.uptime?.formatted || health?.uptime?.formatted || "Unknown"; | |
| return ( | |
| <div className="bg-white/60 dark:bg-gray-900/60 backdrop-blur-md border border-gray-200/50 dark:border-gray-800/50 rounded-2xl p-6 shadow-xl relative overflow-hidden"> | |
| <div className="absolute top-0 right-0 w-24 h-24 bg-primary/5 rounded-full blur-2xl" /> | |
| <div className="absolute bottom-0 left-0 w-32 h-32 bg-emerald-500/5 rounded-full blur-3xl" /> | |
| <div className="flex items-center justify-between mb-6"> | |
| <div> | |
| <h2 className="text-lg font-semibold flex items-center gap-2"> | |
| <Activity className="w-5 h-5 text-primary" /> | |
| System Status | |
| </h2> | |
| <p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">Real-time JVM and resource health</p> | |
| </div> | |
| <button | |
| onClick={fetchHealth} | |
| className="p-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-500 dark:text-gray-400 transition" | |
| title="Refresh health status" | |
| > | |
| <RefreshCw className="w-4 h-4" /> | |
| </button> | |
| </div> | |
| <div className="space-y-4"> | |
| {/* Core Services */} | |
| <div className="grid grid-cols-2 gap-3"> | |
| <div className="p-3 bg-gray-50/50 dark:bg-gray-800/30 border border-gray-100 dark:border-gray-800/50 rounded-xl flex flex-col justify-between"> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-xs text-gray-500 font-medium">Database</span> | |
| {isDbUp ? ( | |
| <CheckCircle className="w-4 h-4 text-emerald-500 shrink-0" /> | |
| ) : ( | |
| <AlertCircle className="w-4 h-4 text-red-500 shrink-0" /> | |
| )} | |
| </div> | |
| <div className="mt-2"> | |
| <p className="text-sm font-semibold">{isDbUp ? "Connected" : "Disconnected"}</p> | |
| <p className="text-[10px] text-gray-400 truncate max-w-full"> | |
| {health?.details?.database?.product || "MySQL / JPA"} | |
| </p> | |
| </div> | |
| </div> | |
| <div className="p-3 bg-gray-50/50 dark:bg-gray-800/30 border border-gray-100 dark:border-gray-800/50 rounded-xl flex flex-col justify-between"> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-xs text-gray-500 font-medium">Storage</span> | |
| {isDiskUp ? ( | |
| <CheckCircle className="w-4 h-4 text-emerald-500 shrink-0" /> | |
| ) : ( | |
| <AlertCircle className="w-4 h-4 text-red-500 shrink-0" /> | |
| )} | |
| </div> | |
| <div className="mt-2"> | |
| <p className="text-sm font-semibold">{isDiskUp ? "Operational" : "Degraded"}</p> | |
| <p className="text-[10px] text-gray-400"> | |
| Free: {health?.checks?.diskSpace?.free || "15.4 GB"} | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Detailed Stats if available */} | |
| {!health?.basic && health?.details && ( | |
| <div className="space-y-3 pt-2"> | |
| {/* Memory usage */} | |
| <div className="space-y-1"> | |
| <div className="flex justify-between text-xs font-medium"> | |
| <span className="text-gray-500 flex items-center gap-1"> | |
| <Database className="w-3.5 h-3.5" /> Memory | |
| </span> | |
| <span>{system.memoryUsedPercent || "0%"} ({system.usedMemory || "0"} / {system.maxMemory || "0"})</span> | |
| </div> | |
| <div className="w-full bg-gray-100 dark:bg-gray-800 rounded-full h-2 overflow-hidden"> | |
| <motion.div | |
| initial={{ width: 0 }} | |
| animate={{ width: system.memoryUsedPercent || "0%" }} | |
| transition={{ duration: 0.5 }} | |
| className="bg-gradient-to-r from-primary to-violet-500 h-full rounded-full" | |
| /> | |
| </div> | |
| </div> | |
| {/* CPU/Processors */} | |
| <div className="flex items-center justify-between text-xs py-1.5 border-b border-gray-100 dark:border-gray-800/50"> | |
| <span className="text-gray-500 flex items-center gap-1"> | |
| <Cpu className="w-3.5 h-3.5" /> CPU Cores | |
| </span> | |
| <span className="font-semibold text-gray-700 dark:text-gray-300"> | |
| {system.availableProcessors || "Unknown"} | |
| </span> | |
| </div> | |
| {/* Active threads */} | |
| <div className="flex items-center justify-between text-xs py-1.5 border-b border-gray-100 dark:border-gray-800/50"> | |
| <span className="text-gray-500 flex items-center gap-1"> | |
| <Server className="w-3.5 h-3.5" /> JVM Threads | |
| </span> | |
| <span className="font-semibold text-gray-700 dark:text-gray-300"> | |
| {jvm.threads || "Unknown"} | |
| </span> | |
| </div> | |
| </div> | |
| )} | |
| {/* Uptime */} | |
| <div className="flex items-center justify-between text-xs pt-1"> | |
| <span className="text-gray-500">Platform Uptime</span> | |
| <span className="font-semibold text-gray-800 dark:text-gray-200 bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded"> | |
| {uptimeStr} | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } |
Xet Storage Details
- Size:
- 7.55 kB
- Xet hash:
- a03c155e759d98c7212f7420b77e48b6bc76eae81b8e918228a15479e2010f6d
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.