Feat: Add System Control (Standby Mode) and Resource Monitor
This commit is contained in:
131
src/components/admin/SystemControl.tsx
Normal file
131
src/components/admin/SystemControl.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Activity, Power, Cpu, Server } from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
interface SystemMetrics {
|
||||
cpu: number;
|
||||
memoryMB: number;
|
||||
uptime: number;
|
||||
state: 'active' | 'standby';
|
||||
}
|
||||
|
||||
export default function SystemControl() {
|
||||
const [metrics, setMetrics] = useState<SystemMetrics | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const fetchMetrics = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/god/system/control');
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
setMetrics(data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch metrics", e);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSystem = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await fetch('/api/god/system/control', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ action: 'toggle' }),
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
if (res.ok) {
|
||||
await fetchMetrics(); // Refresh immediately
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to toggle", e);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Poll every 2 seconds
|
||||
useEffect(() => {
|
||||
fetchMetrics();
|
||||
const interval = setInterval(fetchMetrics, 2000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const isActive = metrics?.state === 'active';
|
||||
|
||||
return (
|
||||
<Card className="border-slate-800 bg-slate-950/50 backdrop-blur">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<div className="space-y-1">
|
||||
<CardTitle className="text-xl font-bold flex items-center gap-2">
|
||||
<Server className="h-5 w-5 text-indigo-400" />
|
||||
Core System Control
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Monitor resource usage and toggle processing engine.
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant={isActive ? 'default' : 'destructive'}
|
||||
className={isActive ? 'bg-green-500/10 text-green-500' : 'bg-red-500/10 text-red-500'}>
|
||||
{isActive ? 'ONLINE' : 'STANDBY'}
|
||||
</Badge>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
|
||||
{/* CPU Monitor */}
|
||||
<div className="p-4 rounded-lg bg-slate-900 border border-slate-800 flex flex-col justify-between">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium text-slate-400">CPU Usage</span>
|
||||
<Cpu className="h-4 w-4 text-blue-400" />
|
||||
</div>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-2xl font-bold text-slate-100">{metrics?.cpu || 0}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-slate-800 h-1.5 mt-2 rounded-full overflow-hidden">
|
||||
<div className="bg-blue-500 h-full transition-all duration-500" style={{ width: `${Math.min(metrics?.cpu || 0, 100)}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* RAM Monitor */}
|
||||
<div className="p-4 rounded-lg bg-slate-900 border border-slate-800 flex flex-col justify-between">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium text-slate-400">RAM Usage</span>
|
||||
<Activity className="h-4 w-4 text-purple-400" />
|
||||
</div>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-2xl font-bold text-slate-100">{metrics?.memoryMB || 0} MB</span>
|
||||
</div>
|
||||
<div className="w-full bg-slate-800 h-1.5 mt-2 rounded-full overflow-hidden">
|
||||
{/* Assumes 2GB typical limit for visualization, though actual is 16GB */}
|
||||
<div className="bg-purple-500 h-full transition-all duration-500" style={{ width: `${Math.min((metrics?.memoryMB || 0) / 2048 * 100, 100)}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Master Switch */}
|
||||
<div className="flex items-center justify-center">
|
||||
<Button
|
||||
variant={isActive ? 'destructive' : 'default'}
|
||||
size="lg"
|
||||
className={`w-full h-full min-h-[100px] text-lg font-bold shadow-lg transition-all ${isActive
|
||||
? 'bg-red-500 hover:bg-red-600 shadow-red-900/20'
|
||||
: 'bg-green-500 hover:bg-green-600 shadow-green-900/20'
|
||||
}`}
|
||||
onClick={toggleSystem}
|
||||
disabled={loading}
|
||||
>
|
||||
<Power className="mr-2 h-6 w-6" />
|
||||
{isActive ? 'DEACTIVATE ENGINE' : 'ACTIVATE ENGINE'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-slate-500 text-center">
|
||||
* Activating the engine will enable heavy resource consumption (`npm` processes). Deactivating puts the system in standby, reducing CPU/RAM usage.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user