feat: detail chart history point

This commit is contained in:
hamster1963 2024-12-26 13:48:19 +08:00
parent a9a7a367c0
commit 72472f7cc1
2 changed files with 188 additions and 33 deletions

View File

@ -1,6 +1,6 @@
"use client" "use client"
import { useServerData } from "@/app/lib/server-data-context" import { ServerDataWithTimestamp, useServerData } from "@/app/lib/server-data-context"
import { NezhaAPISafe } from "@/app/types/nezha-api" import { NezhaAPISafe } from "@/app/types/nezha-api"
import { ServerDetailChartLoading } from "@/components/loading/ServerDetailLoading" import { ServerDetailChartLoading } from "@/components/loading/ServerDetailLoading"
import AnimatedCircularProgressBar from "@/components/ui/animated-circular-progress-bar" import AnimatedCircularProgressBar from "@/components/ui/animated-circular-progress-bar"
@ -8,7 +8,7 @@ import { Card, CardContent } from "@/components/ui/card"
import { ChartConfig, ChartContainer } from "@/components/ui/chart" import { ChartConfig, ChartContainer } from "@/components/ui/chart"
import { formatBytes, formatNezhaInfo, formatRelativeTime } from "@/lib/utils" import { formatBytes, formatNezhaInfo, formatRelativeTime } from "@/lib/utils"
import { useTranslations } from "next-intl" import { useTranslations } from "next-intl"
import { useEffect, useState } from "react" import { useEffect, useRef, useState } from "react"
import { Area, AreaChart, CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts" import { Area, AreaChart, CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"
type cpuChartData = { type cpuChartData = {
@ -52,7 +52,7 @@ export default function ServerDetailChartClient({
}) { }) {
const t = useTranslations("ServerDetailChartClient") const t = useTranslations("ServerDetailChartClient")
const { data: serverList, error } = useServerData() const { data: serverList, error, history } = useServerData()
const data = serverList?.result?.find((item) => item.id === server_id) const data = serverList?.result?.find((item) => item.id === server_id)
@ -70,23 +70,46 @@ export default function ServerDetailChartClient({
return ( return (
<section className="grid md:grid-cols-2 lg:grid-cols-3 grid-cols-1 gap-3"> <section className="grid md:grid-cols-2 lg:grid-cols-3 grid-cols-1 gap-3">
<CpuChart data={data} /> <CpuChart data={data} history={history} />
<ProcessChart data={data} /> <ProcessChart data={data} history={history} />
<DiskChart data={data} /> <DiskChart data={data} history={history} />
<MemChart data={data} /> <MemChart data={data} history={history} />
<NetworkChart data={data} /> <NetworkChart data={data} history={history} />
<ConnectChart data={data} /> <ConnectChart data={data} history={history} />
</section> </section>
) )
} }
function CpuChart({ data }: { data: NezhaAPISafe }) { function CpuChart({ history, data }: { history: ServerDataWithTimestamp[]; data: NezhaAPISafe }) {
const [cpuChartData, setCpuChartData] = useState([] as cpuChartData[]) const [cpuChartData, setCpuChartData] = useState([] as cpuChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
useEffect(() => {
if (!hasInitialized.current && history.length > 0) {
const historyData = history
.map((msg) => {
const server = msg.data?.result?.find((item) => item.id === data.id)
if (!server) return null
const { cpu } = formatNezhaInfo(server)
return {
timeStamp: msg.timestamp.toString(),
cpu: cpu,
}
})
.filter((item): item is cpuChartData => item !== null)
.reverse() // 保持时间顺序
setCpuChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
}
}, [])
const { cpu } = formatNezhaInfo(data) const { cpu } = formatNezhaInfo(data)
useEffect(() => { useEffect(() => {
if (data) { if (data && historyLoaded) {
const timestamp = Date.now().toString() const timestamp = Date.now().toString()
let newData = [] as cpuChartData[] let newData = [] as cpuChartData[]
if (cpuChartData.length === 0) { if (cpuChartData.length === 0) {
@ -102,7 +125,7 @@ function CpuChart({ data }: { data: NezhaAPISafe }) {
} }
setCpuChartData(newData) setCpuChartData(newData)
} }
}, [data]) }, [data, historyLoaded])
const chartConfig = { const chartConfig = {
cpu: { cpu: {
@ -171,15 +194,43 @@ function CpuChart({ data }: { data: NezhaAPISafe }) {
) )
} }
function ProcessChart({ data }: { data: NezhaAPISafe }) { function ProcessChart({
data,
history,
}: {
data: NezhaAPISafe
history: ServerDataWithTimestamp[]
}) {
const t = useTranslations("ServerDetailChartClient") const t = useTranslations("ServerDetailChartClient")
const [processChartData, setProcessChartData] = useState([] as processChartData[]) const [processChartData, setProcessChartData] = useState([] as processChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
useEffect(() => {
if (!hasInitialized.current && history.length > 0) {
const historyData = history
.map((msg) => {
const server = msg.data?.result?.find((item) => item.id === data.id)
if (!server) return null
const { process } = formatNezhaInfo(server)
return {
timeStamp: msg.timestamp.toString(),
process: process,
}
})
.filter((item): item is processChartData => item !== null)
.reverse()
setProcessChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
}
}, [])
const { process } = formatNezhaInfo(data) const { process } = formatNezhaInfo(data)
useEffect(() => { useEffect(() => {
if (data) { if (data && historyLoaded) {
const timestamp = Date.now().toString() const timestamp = Date.now().toString()
let newData = [] as processChartData[] let newData = [] as processChartData[]
if (processChartData.length === 0) { if (processChartData.length === 0) {
@ -195,7 +246,7 @@ function ProcessChart({ data }: { data: NezhaAPISafe }) {
} }
setProcessChartData(newData) setProcessChartData(newData)
} }
}, [data]) }, [data, historyLoaded])
const chartConfig = { const chartConfig = {
process: { process: {
@ -250,15 +301,38 @@ function ProcessChart({ data }: { data: NezhaAPISafe }) {
) )
} }
function MemChart({ data }: { data: NezhaAPISafe }) { function MemChart({ data, history }: { data: NezhaAPISafe; history: ServerDataWithTimestamp[] }) {
const t = useTranslations("ServerDetailChartClient") const t = useTranslations("ServerDetailChartClient")
const [memChartData, setMemChartData] = useState([] as memChartData[]) const [memChartData, setMemChartData] = useState([] as memChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
useEffect(() => {
if (!hasInitialized.current && history.length > 0) {
const historyData = history
.map((msg) => {
const server = msg.data?.result?.find((item) => item.id === data.id)
if (!server) return null
const { mem, swap } = formatNezhaInfo(server)
return {
timeStamp: msg.timestamp.toString(),
mem: mem,
swap: swap,
}
})
.filter((item): item is memChartData => item !== null)
.reverse()
setMemChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
}
}, [])
const { mem, swap } = formatNezhaInfo(data) const { mem, swap } = formatNezhaInfo(data)
useEffect(() => { useEffect(() => {
if (data) { if (data && historyLoaded) {
const timestamp = Date.now().toString() const timestamp = Date.now().toString()
let newData = [] as memChartData[] let newData = [] as memChartData[]
if (memChartData.length === 0) { if (memChartData.length === 0) {
@ -274,7 +348,7 @@ function MemChart({ data }: { data: NezhaAPISafe }) {
} }
setMemChartData(newData) setMemChartData(newData)
} }
}, [data]) }, [data, historyLoaded])
const chartConfig = { const chartConfig = {
mem: { mem: {
@ -379,15 +453,37 @@ function MemChart({ data }: { data: NezhaAPISafe }) {
) )
} }
function DiskChart({ data }: { data: NezhaAPISafe }) { function DiskChart({ data, history }: { data: NezhaAPISafe; history: ServerDataWithTimestamp[] }) {
const t = useTranslations("ServerDetailChartClient") const t = useTranslations("ServerDetailChartClient")
const [diskChartData, setDiskChartData] = useState([] as diskChartData[]) const [diskChartData, setDiskChartData] = useState([] as diskChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
useEffect(() => {
if (!hasInitialized.current && history.length > 0) {
const historyData = history
.map((msg) => {
const server = msg.data?.result?.find((item) => item.id === data.id)
if (!server) return null
const { disk } = formatNezhaInfo(server)
return {
timeStamp: msg.timestamp.toString(),
disk: disk,
}
})
.filter((item): item is diskChartData => item !== null)
.reverse()
setDiskChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
}
}, [])
const { disk } = formatNezhaInfo(data) const { disk } = formatNezhaInfo(data)
useEffect(() => { useEffect(() => {
if (data) { if (data && historyLoaded) {
const timestamp = Date.now().toString() const timestamp = Date.now().toString()
let newData = [] as diskChartData[] let newData = [] as diskChartData[]
if (diskChartData.length === 0) { if (diskChartData.length === 0) {
@ -403,7 +499,7 @@ function DiskChart({ data }: { data: NezhaAPISafe }) {
} }
setDiskChartData(newData) setDiskChartData(newData)
} }
}, [data]) }, [data, historyLoaded])
const chartConfig = { const chartConfig = {
disk: { disk: {
@ -477,15 +573,44 @@ function DiskChart({ data }: { data: NezhaAPISafe }) {
) )
} }
function NetworkChart({ data }: { data: NezhaAPISafe }) { function NetworkChart({
data,
history,
}: {
data: NezhaAPISafe
history: ServerDataWithTimestamp[]
}) {
const t = useTranslations("ServerDetailChartClient") const t = useTranslations("ServerDetailChartClient")
const [networkChartData, setNetworkChartData] = useState([] as networkChartData[]) const [networkChartData, setNetworkChartData] = useState([] as networkChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
useEffect(() => {
if (!hasInitialized.current && history.length > 0) {
const historyData = history
.map((msg) => {
const server = msg.data?.result?.find((item) => item.id === data.id)
if (!server) return null
const { up, down } = formatNezhaInfo(server)
return {
timeStamp: msg.timestamp.toString(),
upload: up,
download: down,
}
})
.filter((item): item is networkChartData => item !== null)
.reverse()
setNetworkChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
}
}, [])
const { up, down } = formatNezhaInfo(data) const { up, down } = formatNezhaInfo(data)
useEffect(() => { useEffect(() => {
if (data) { if (data && historyLoaded) {
const timestamp = Date.now().toString() const timestamp = Date.now().toString()
let newData = [] as networkChartData[] let newData = [] as networkChartData[]
if (networkChartData.length === 0) { if (networkChartData.length === 0) {
@ -501,7 +626,7 @@ function NetworkChart({ data }: { data: NezhaAPISafe }) {
} }
setNetworkChartData(newData) setNetworkChartData(newData)
} }
}, [data]) }, [data, historyLoaded])
let maxDownload = Math.max(...networkChartData.map((item) => item.download)) let maxDownload = Math.max(...networkChartData.map((item) => item.download))
maxDownload = Math.ceil(maxDownload) maxDownload = Math.ceil(maxDownload)
@ -595,13 +720,43 @@ function NetworkChart({ data }: { data: NezhaAPISafe }) {
) )
} }
function ConnectChart({ data }: { data: NezhaAPISafe }) { function ConnectChart({
data,
history,
}: {
data: NezhaAPISafe
history: ServerDataWithTimestamp[]
}) {
const [connectChartData, setConnectChartData] = useState([] as connectChartData[]) const [connectChartData, setConnectChartData] = useState([] as connectChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
useEffect(() => {
if (!hasInitialized.current && history.length > 0) {
const historyData = history
.map((msg) => {
const server = msg.data?.result?.find((item) => item.id === data.id)
if (!server) return null
const { tcp, udp } = formatNezhaInfo(server)
return {
timeStamp: msg.timestamp.toString(),
tcp: tcp,
udp: udp,
}
})
.filter((item): item is connectChartData => item !== null)
.reverse()
setConnectChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
}
}, [])
const { tcp, udp } = formatNezhaInfo(data) const { tcp, udp } = formatNezhaInfo(data)
useEffect(() => { useEffect(() => {
if (data) { if (data && historyLoaded) {
const timestamp = Date.now().toString() const timestamp = Date.now().toString()
let newData = [] as connectChartData[] let newData = [] as connectChartData[]
if (connectChartData.length === 0) { if (connectChartData.length === 0) {
@ -617,7 +772,7 @@ function ConnectChart({ data }: { data: NezhaAPISafe }) {
} }
setConnectChartData(newData) setConnectChartData(newData)
} }
}, [data]) }, [data, historyLoaded])
const chartConfig = { const chartConfig = {
tcp: { tcp: {

View File

@ -6,7 +6,7 @@ import { nezhaFetcher } from "@/lib/utils"
import { ReactNode, createContext, useContext, useEffect, useState } from "react" import { ReactNode, createContext, useContext, useEffect, useState } from "react"
import useSWR from "swr" import useSWR from "swr"
interface ServerDataWithTimestamp { export interface ServerDataWithTimestamp {
timestamp: number timestamp: number
data: ServerApi data: ServerApi
} }
@ -27,7 +27,7 @@ export function ServerDataProvider({ children }: { children: ReactNode }) {
const { data, error, isLoading } = useSWR<ServerApi>("/api/server", nezhaFetcher, { const { data, error, isLoading } = useSWR<ServerApi>("/api/server", nezhaFetcher, {
refreshInterval: Number(getEnv("NEXT_PUBLIC_NezhaFetchInterval")) || 2000, refreshInterval: Number(getEnv("NEXT_PUBLIC_NezhaFetchInterval")) || 2000,
dedupingInterval: 500, dedupingInterval: 1000,
}) })
useEffect(() => { useEffect(() => {