"use client"; import AnimatedCircularProgressBar from "@/components/ui/animated-circular-progress-bar"; import { Card, CardContent } from "@/components/ui/card"; import { ChartConfig, ChartContainer } from "@/components/ui/chart"; import getEnv from "@/lib/env-entry"; import { formatNezhaInfo, formatRelativeTime, nezhaFetcher } from "@/lib/utils"; import { useTranslations } from "next-intl"; import { useEffect, useState } from "react"; import { Area, AreaChart, CartesianGrid, Line, LineChart, XAxis, YAxis, } from "recharts"; import useSWR from "swr"; import { NezhaAPISafe } from "../../types/nezha-api"; type cpuChartData = { timeStamp: string; cpu: number; }; type processChartData = { timeStamp: string; process: number; }; type diskChartData = { timeStamp: string; disk: number; }; type memChartData = { timeStamp: string; mem: number; swap: number; }; type networkChartData = { timeStamp: string; upload: number; download: number; }; type connectChartData = { timeStamp: string; tcp: number; udp: number; }; export default function ServerDetailChartClient({ server_id, }: { server_id: number; }) { const t = useTranslations("ServerDetailChartClient"); const { data, error } = useSWR( `/api/detail?server_id=${server_id}`, nezhaFetcher, { refreshInterval: Number(getEnv("NEXT_PUBLIC_NezhaFetchInterval")) || 5000, }, ); if (error) { return ( <>

{error.message}

{t("chart_fetch_error_message")}

); } if (!data) return null; return (
); } function CpuChart({ data }: { data: NezhaAPISafe }) { const [cpuChartData, setCpuChartData] = useState([] as cpuChartData[]); const { cpu } = formatNezhaInfo(data); useEffect(() => { if (data) { const timestamp = Date.now().toString(); let newData = [] as cpuChartData[]; if (cpuChartData.length === 0) { newData = [{ timeStamp: timestamp, cpu: cpu }, { timeStamp: timestamp, cpu: cpu }]; } else { newData = [...cpuChartData, { timeStamp: timestamp, cpu: cpu }]; } if (newData.length > 30) { newData.shift(); } setCpuChartData(newData); } }, [data]); const chartConfig = { cpu: { label: "CPU", }, } satisfies ChartConfig; return (

CPU

{cpu.toFixed(0)}%

formatRelativeTime(value)} /> `${value}%`} />
); } function ProcessChart({ data }: { data: NezhaAPISafe }) { const t = useTranslations("ServerDetailChartClient"); const [processChartData, setProcessChartData] = useState( [] as processChartData[], ); const { process } = formatNezhaInfo(data); useEffect(() => { if (data) { const timestamp = Date.now().toString(); let newData = [] as processChartData[]; if (processChartData.length === 0) { newData = [{ timeStamp: timestamp, process: process }, { timeStamp: timestamp, process: process }]; } else { newData = [...processChartData, { timeStamp: timestamp, process: process }]; } if (newData.length > 30) { newData.shift(); } setProcessChartData(newData); } }, [data]); const chartConfig = { process: { label: "Process", }, } satisfies ChartConfig; return (

{t("Process")}

{process}

formatRelativeTime(value)} />
); } function MemChart({ data }: { data: NezhaAPISafe }) { const t = useTranslations("ServerDetailChartClient"); const [memChartData, setMemChartData] = useState([] as memChartData[]); const { mem, swap } = formatNezhaInfo(data); useEffect(() => { if (data) { const timestamp = Date.now().toString(); let newData = [] as memChartData[]; if (memChartData.length === 0) { newData = [{ timeStamp: timestamp, mem: mem, swap: swap }, { timeStamp: timestamp, mem: mem, swap: swap }]; } else { newData = [...memChartData, { timeStamp: timestamp, mem: mem, swap: swap }]; } if (newData.length > 30) { newData.shift(); } setMemChartData(newData); } }, [data]); const chartConfig = { mem: { label: "Mem", }, swap: { label: "Swap", }, } satisfies ChartConfig; return (

{t("Mem")}

{mem.toFixed(0)}%

{t("Swap")}

{swap.toFixed(0)}%

formatRelativeTime(value)} /> `${value}%`} />
); } function DiskChart({ data }: { data: NezhaAPISafe }) { const t = useTranslations("ServerDetailChartClient"); const [diskChartData, setDiskChartData] = useState([] as diskChartData[]); const { disk } = formatNezhaInfo(data); useEffect(() => { if (data) { const timestamp = Date.now().toString(); let newData = [] as diskChartData[]; if (diskChartData.length === 0) { newData = [{ timeStamp: timestamp, disk: disk }, { timeStamp: timestamp, disk: disk }]; } else { newData = [...diskChartData, { timeStamp: timestamp, disk: disk }]; } if (newData.length > 30) { newData.shift(); } setDiskChartData(newData); } }, [data]); const chartConfig = { disk: { label: "Disk", }, } satisfies ChartConfig; return (

{t("Disk")}

{disk.toFixed(0)}%

formatRelativeTime(value)} /> `${value}%`} />
); } function NetworkChart({ data }: { data: NezhaAPISafe }) { const t = useTranslations("ServerDetailChartClient"); const [networkChartData, setNetworkChartData] = useState( [] as networkChartData[], ); const { up, down } = formatNezhaInfo(data); useEffect(() => { if (data) { const timestamp = Date.now().toString(); let newData = [] as networkChartData[]; if (networkChartData.length === 0) { newData = [{ timeStamp: timestamp, upload: up, download: down }, { timeStamp: timestamp, upload: up, download: down }]; } else { newData = [...networkChartData, { timeStamp: timestamp, upload: up, download: down }]; } if (newData.length > 30) { newData.shift(); } setNetworkChartData(newData); } }, [data]); let maxDownload = Math.max(...networkChartData.map((item) => item.download)); maxDownload = Math.ceil(maxDownload); if (maxDownload < 1) { maxDownload = 1; } const chartConfig = { upload: { label: "Upload", }, download: { label: "Download", }, } satisfies ChartConfig; return (

{t("Upload")}

{up.toFixed(2)} M/s

{t("Download")}

{down.toFixed(2)} M/s

formatRelativeTime(value)} /> `${value.toFixed(0)}M/s`} />
); } function ConnectChart({ data }: { data: NezhaAPISafe }) { const [connectChartData, setConnectChartData] = useState( [] as connectChartData[], ); const { tcp, udp } = formatNezhaInfo(data); useEffect(() => { if (data) { const timestamp = Date.now().toString(); let newData = [] as connectChartData[]; if (connectChartData.length === 0) { newData = [{ timeStamp: timestamp, tcp: tcp, udp: udp }, { timeStamp: timestamp, tcp: tcp, udp: udp }]; } else { newData = [...connectChartData, { timeStamp: timestamp, tcp: tcp, udp: udp }]; } if (newData.length > 30) { newData.shift(); } setConnectChartData(newData); } }, [data]); const chartConfig = { tcp: { label: "TCP", }, udp: { label: "UDP", }, } satisfies ChartConfig; return (

TCP

{tcp}

UDP

{udp}

formatRelativeTime(value)} />
); }