From 6e676022e9bb4752ac74c64e5fbcc33e92eaf1c0 Mon Sep 17 00:00:00 2001 From: hamster1963 <1410514192@qq.com> Date: Fri, 29 Nov 2024 10:00:48 +0800 Subject: [PATCH] feat: cycleTransferStats --- src/components/CycleTransferStats.tsx | 42 +++++++++++ src/components/CycleTransferStatsClient.tsx | 79 +++++++++++++++++++++ src/components/ServiceTracker.tsx | 45 ++++++++---- src/components/ServiceTrackerClient.tsx | 15 +++- src/lib/format.ts | 11 +++ src/types/nezha-api.ts | 22 ++++++ 6 files changed, 198 insertions(+), 16 deletions(-) create mode 100644 src/components/CycleTransferStats.tsx create mode 100644 src/components/CycleTransferStatsClient.tsx create mode 100644 src/lib/format.ts diff --git a/src/components/CycleTransferStats.tsx b/src/components/CycleTransferStats.tsx new file mode 100644 index 0000000..5bbf753 --- /dev/null +++ b/src/components/CycleTransferStats.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import { CycleTransferStats } from "@/types/nezha-api"; +import { CycleTransferStatsClient } from "./CycleTransferStatsClient"; + +interface CycleTransferStatsProps { + cycleStats: CycleTransferStats; + className?: string; +} + +export const CycleTransferStatsCard: React.FC = ({ + cycleStats, + className, +}) => { + return ( + <> + {Object.entries(cycleStats).map(([cycleId, cycleData]) => { + const serverStats = Object.entries(cycleData.server_name).map( + ([serverId, serverName]) => ({ + serverId, + serverName, + transfer: cycleData.transfer[serverId] || 0, + nextUpdate: cycleData.next_update[serverId], + }), + ); + + return ( + + ); + })} + + ); +}; + +export default CycleTransferStatsCard; diff --git a/src/components/CycleTransferStatsClient.tsx b/src/components/CycleTransferStatsClient.tsx new file mode 100644 index 0000000..1e51e0e --- /dev/null +++ b/src/components/CycleTransferStatsClient.tsx @@ -0,0 +1,79 @@ +import React from "react"; +import { cn } from "@/lib/utils"; +import { formatBytes } from "@/lib/format"; + +interface CycleTransferStatsClientProps { + name: string; + from: string; + to: string; + max: number; + serverStats: Array<{ + serverId: string; + serverName: string; + transfer: number; + nextUpdate: string; + }>; + className?: string; +} + +export const CycleTransferStatsClient: React.FC< + CycleTransferStatsClientProps +> = ({ name, from, to, max, serverStats, className }) => { + return ( +
+
+ {/*
+ {name} + + {new Date(from).toLocaleDateString()} -{" "} + {new Date(to).toLocaleDateString()} + +
*/} +
+ {serverStats.map(({ serverId, serverName, transfer, nextUpdate }) => { + const progress = (transfer / max) * 100; + + return ( +
+
+ {serverName} + + {formatBytes(transfer)} / {formatBytes(max)} + +
+
+
+
+ +
+
+ Next update: {new Date(nextUpdate).toLocaleString()} +
+
+ + {new Date(from).toLocaleDateString()} -{" "} + {new Date(to).toLocaleDateString()} + +
+ {name} +
+
+
+
+ ); + })} +
+
+
+ ); +}; + +export default CycleTransferStatsClient; diff --git a/src/components/ServiceTracker.tsx b/src/components/ServiceTracker.tsx index da65b66..fda7abb 100644 --- a/src/components/ServiceTracker.tsx +++ b/src/components/ServiceTracker.tsx @@ -3,6 +3,7 @@ import ServiceTrackerClient from "./ServiceTrackerClient"; import { useQuery } from "@tanstack/react-query"; import { fetchService } from "@/lib/nezha-api"; import { ServiceData } from "@/types/nezha-api"; +import { CycleTransferStatsCard } from "./CycleTransferStats"; export const ServiceTracker: React.FC = () => { const { data: serviceData, isLoading } = useQuery({ @@ -25,7 +26,13 @@ export const ServiceTracker: React.FC = () => { serviceData.down.reduce((a, b) => a + b, 0); const uptime = (totalUp / totalChecks) * 100; - return { days, uptime }; + const avgDelay = + serviceData.delay.length > 0 + ? serviceData.delay.reduce((a, b) => a + b, 0) / + serviceData.delay.length + : 0; + + return { days, uptime, avgDelay }; }; if (isLoading) { @@ -37,18 +44,30 @@ export const ServiceTracker: React.FC = () => { } return ( -
- {Object.entries(serviceData.data.services).map(([name, data]) => { - const { days, uptime } = processServiceData(data); - return ( - - ); - })} +
+
+ {serviceData.data.cycle_transfer_stats && ( +
+ +
+ )} +
+
+ {Object.entries(serviceData.data.services).map(([name, data]) => { + const { days, uptime, avgDelay } = processServiceData(data); + return ( + + ); + })} +
); }; diff --git a/src/components/ServiceTrackerClient.tsx b/src/components/ServiceTrackerClient.tsx index eb1d5a7..e0fde6d 100644 --- a/src/components/ServiceTrackerClient.tsx +++ b/src/components/ServiceTrackerClient.tsx @@ -1,5 +1,6 @@ import React from "react"; import { cn } from "@/lib/utils"; +import { Separator } from "./ui/separator"; interface ServiceTrackerProps { days: Array<{ @@ -9,6 +10,7 @@ interface ServiceTrackerProps { className?: string; title?: string; uptime?: number; + avgDelay?: number; } export const ServiceTrackerClient: React.FC = ({ @@ -16,6 +18,7 @@ export const ServiceTrackerClient: React.FC = ({ className, title, uptime = 100, + avgDelay = 0, }) => { return (
= ({
{title}
- - {uptime.toFixed(1)}% uptime - +
+ + {avgDelay.toFixed(0)}ms + + + + {uptime.toFixed(1)}% uptime + +
diff --git a/src/lib/format.ts b/src/lib/format.ts new file mode 100644 index 0000000..47a3b4e --- /dev/null +++ b/src/lib/format.ts @@ -0,0 +1,11 @@ +export function formatBytes(bytes: number, decimals = 2): string { + if (bytes === 0) return "0 B"; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; +} diff --git a/src/types/nezha-api.ts b/src/types/nezha-api.ts index 11b63d9..f702221 100644 --- a/src/types/nezha-api.ts +++ b/src/types/nezha-api.ts @@ -98,6 +98,7 @@ export interface ServiceResponse { services: { [key: string]: ServiceData; }; + cycle_transfer_stats: CycleTransferStats; }; } @@ -127,3 +128,24 @@ export interface ServiceData { up: number[]; down: number[]; } + +export interface CycleTransferStats { + [key: string]: CycleTransferData; +} + +export interface CycleTransferData { + name: string; + from: string; + to: string; + max: number; + min: number; + server_name: { + [key: string]: string; + }; + transfer: { + [key: string]: number; + }; + next_update: { + [key: string]: string; + }; +}