feat: add system, uptime, network and processes widgets

Add four new server statistics widgets:
- SystemWidget: displays hostname, OS, and kernel information
- UptimeWidget: shows server total uptime with formatted display
- NetworkWidget: lists network interfaces with IP and status
- ProcessesWidget: displays top processes by CPU usage

Backend changes:
- Extended SSH metrics collection to gather network, uptime, process, and system data
- Added commands to parse /proc/uptime, ip addr, ps aux output

Frontend changes:
- Created 4 new widget components with consistent styling
- Updated widget type definitions and HostManagerEditor
- Unified all widget heights to 280px for consistent layout
- Added translations for all new widgets (EN/ZH)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZacharyZcR
2025-10-09 13:21:09 +08:00
parent d87cabb708
commit d740abd0e8
11 changed files with 600 additions and 14 deletions

View File

@@ -0,0 +1,64 @@
import React from "react";
import { Network, Wifi, WifiOff } from "lucide-react";
import { useTranslation } from "react-i18next";
import type { ServerMetrics } from "@/ui/main-axios.ts";
interface NetworkWidgetProps {
metrics: ServerMetrics | null;
metricsHistory: ServerMetrics[];
}
export function NetworkWidget({ metrics }: NetworkWidgetProps) {
const { t } = useTranslation();
const network = (metrics as any)?.network;
const interfaces = network?.interfaces || [];
return (
<div className="h-full w-full p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200 flex flex-col overflow-hidden">
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
<Network className="h-5 w-5 text-indigo-400" />
<h3 className="font-semibold text-lg text-white">
{t("serverStats.networkInterfaces")}
</h3>
</div>
<div className="space-y-2 overflow-auto flex-1">
{interfaces.length === 0 ? (
<div className="flex flex-col items-center justify-center py-8 text-gray-400">
<WifiOff className="h-8 w-8 mb-2" />
<p className="text-sm">{t("serverStats.noInterfacesFound")}</p>
</div>
) : (
interfaces.map((iface: any, index: number) => (
<div
key={index}
className="p-3 rounded-md bg-dark-bg/50 border border-dark-border/30"
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<Wifi
className={`h-4 w-4 ${iface.state === "UP" ? "text-green-400" : "text-gray-400"}`}
/>
<span className="text-sm font-semibold text-white font-mono">
{iface.name}
</span>
</div>
<span
className={`text-xs px-2 py-0.5 rounded-full ${
iface.state === "UP"
? "bg-green-500/20 text-green-400"
: "bg-gray-500/20 text-gray-400"
}`}
>
{iface.state}
</span>
</div>
<div className="text-xs text-gray-400 font-mono">{iface.ip}</div>
</div>
))
)}
</div>
</div>
);
}