diff --git a/.github/get-token.png b/.github/get-token.png deleted file mode 100644 index 4b21dda..0000000 Binary files a/.github/get-token.png and /dev/null differ diff --git a/.github/shot-1-dark.png b/.github/shot-1-dark.png new file mode 100644 index 0000000..630ba25 Binary files /dev/null and b/.github/shot-1-dark.png differ diff --git a/.github/shot-1.png b/.github/shot-1.png index 80d9343..c1ac9ff 100644 Binary files a/.github/shot-1.png and b/.github/shot-1.png differ diff --git a/.github/shot-2-dark.png b/.github/shot-2-dark.png new file mode 100644 index 0000000..4c3975e Binary files /dev/null and b/.github/shot-2-dark.png differ diff --git a/.github/shot-2.png b/.github/shot-2.png index 9768951..8bd53a7 100644 Binary files a/.github/shot-2.png and b/.github/shot-2.png differ diff --git a/.github/shot-3-dark.png b/.github/shot-3-dark.png new file mode 100644 index 0000000..25288c7 Binary files /dev/null and b/.github/shot-3-dark.png differ diff --git a/.github/shot-3.png b/.github/shot-3.png index 9eb66a7..ba6dae5 100644 Binary files a/.github/shot-3.png and b/.github/shot-3.png differ diff --git a/.github/shot-4.png b/.github/shot-4.png deleted file mode 100644 index 44bf56b..0000000 Binary files a/.github/shot-4.png and /dev/null differ diff --git a/README.md b/README.md index f5402eb..3857359 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,9 @@ | 英语 | en | 是 | | 日语 | ja | 是 | -![screen-shot-one](/.github/shot-1.png) -![screen-shot-two](/.github/shot-2.png) -![screen-shot-three](/.github/shot-3.png) -![screen-shot-four](/.github/shot-4.png) +![screen](/.github/shot-1.png) +![screen](/.github/shot-2.png) +![screen](/.github/shot-3.png) +![screen](/.github/shot-1-dark.png) +![screen](/.github/shot-2-dark.png) +![screen](/.github/shot-3-dark.png) diff --git a/app/[locale]/(main)/ClientComponents/ServerDetailChartClient.tsx b/app/[locale]/(main)/ClientComponents/ServerDetailChartClient.tsx new file mode 100644 index 0000000..dad5f6e --- /dev/null +++ b/app/[locale]/(main)/ClientComponents/ServerDetailChartClient.tsx @@ -0,0 +1,703 @@ +"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(); + const 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(); + const 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(); + const 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(); + const 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(); + const 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(); + const 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)} + /> + + + + + +
+
+
+ ); +} diff --git a/app/[locale]/(main)/ClientComponents/ServerDetailClient.tsx b/app/[locale]/(main)/ClientComponents/ServerDetailClient.tsx new file mode 100644 index 0000000..718f814 --- /dev/null +++ b/app/[locale]/(main)/ClientComponents/ServerDetailClient.tsx @@ -0,0 +1,152 @@ +"use client"; + +import { NezhaAPISafe } from "@/app/[locale]/types/nezha-api"; +import { BackIcon } from "@/components/Icon"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent } from "@/components/ui/card"; +import getEnv from "@/lib/env-entry"; +import { cn, formatBytes, nezhaFetcher } from "@/lib/utils"; +import { useLocale, useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; +import useSWR from "swr"; + +import ServerDetailLoading from "./ServerDetailLoading"; + +export default function ServerDetailClient({ + server_id, +}: { + server_id: number; +}) { + const t = useTranslations("ServerDetailClient"); + const router = useRouter(); + const locale = useLocale(); + const { data, error } = useSWR( + `/api/detail?server_id=${server_id}`, + nezhaFetcher, + { + refreshInterval: Number(getEnv("NEXT_PUBLIC_NezhaFetchInterval")) || 5000, + }, + ); + + if (error) { + return ( + <> +
+

{error.message}

+

+ {t("detail_fetch_error_message")} +

+
+ + ); + } + + if (!data) return ; + + return ( +
+
{ + router.push(`/${locale}/`); + }} + className="flex flex-none cursor-pointer font-semibold leading-none items-center break-all tracking-tight gap-0.5 text-xl" + > + + {data?.name} +
+
+ + +
+

{t("status")}

+ + {data?.online_status ? t("Online") : t("Offline")} + +
+
+
+ + +
+

{t("Uptime")}

+
+ {" "} + {(data?.status.Uptime / 86400).toFixed(0)} {t("Days")}{" "} +
+
+
+
+ + +
+

{t("Version")}

+
{data?.host.Version || "Unknown"}
+
+
+
+ + +
+

{t("Arch")}

+
{data?.host.Arch || "Unknown"}
+
+
+
+ + +
+

{t("Mem")}

+
{formatBytes(data?.host.MemTotal)}
+
+
+
+ + +
+

{t("Disk")}

+
{formatBytes(data?.host.DiskTotal)}
+
+
+
+
+
+ + +
+

{t("System")}

+ {data?.host.Platform ? ( +
+ {" "} + {data?.host.Platform || "Unknown"} -{" "} + {data?.host.PlatformVersion}{" "} +
+ ) : ( +
Unknown
+ )} +
+
+
+ + +
+

{t("CPU")}

+ {data?.host.CPU ? ( +
{data?.host.CPU}
+ ) : ( +
Unknown
+ )} +
+
+
+
+
+ ); +} diff --git a/app/[locale]/(main)/ClientComponents/ServerDetailLoading.tsx b/app/[locale]/(main)/ClientComponents/ServerDetailLoading.tsx new file mode 100644 index 0000000..2dc538b --- /dev/null +++ b/app/[locale]/(main)/ClientComponents/ServerDetailLoading.tsx @@ -0,0 +1,33 @@ +import { BackIcon } from "@/components/Icon"; +import { Separator } from "@/components/ui/separator"; +import { Skeleton } from "@/components/ui/skeleton"; +import { useLocale } from "next-intl"; +import { useRouter } from "next/navigation"; + +export default function ServerDetailLoading() { + const router = useRouter(); + const locale = useLocale(); + return ( +
+
{ + router.push(`/${locale}/`); + }} + className="flex flex-none cursor-pointer font-semibold leading-none items-center break-all tracking-tight gap-0.5 text-xl" + > + + +
+ + +
+ + + + + + +
+
+ ); +} diff --git a/app/[locale]/(main)/ClientComponents/ServerOverviewClient.tsx b/app/[locale]/(main)/ClientComponents/ServerOverviewClient.tsx index a4ebb7e..4cd6c81 100644 --- a/app/[locale]/(main)/ClientComponents/ServerOverviewClient.tsx +++ b/app/[locale]/(main)/ClientComponents/ServerOverviewClient.tsx @@ -15,7 +15,7 @@ export default function ServerOverviewClient() { const { data } = useSWR("/api/server", nezhaFetcher); const disableCartoon = getEnv("NEXT_PUBLIC_DisableCartoon") === "true"; return ( -
+
@@ -94,9 +94,14 @@ export default function ServerOverviewClient() { {t("p_3463-3530_Totalbandwidth")}

{data ? ( -

- {formatBytes(data?.total_bandwidth)} -

+
+

+ ↑{formatBytes(data?.total_out_bandwidth)} +

+

+ ↓{formatBytes(data?.total_in_bandwidth)} +

+
) : (
diff --git a/app/[locale]/(main)/detail/[id]/page.tsx b/app/[locale]/(main)/detail/[id]/page.tsx new file mode 100644 index 0000000..de69a5d --- /dev/null +++ b/app/[locale]/(main)/detail/[id]/page.tsx @@ -0,0 +1,13 @@ +import ServerDetailChartClient from "@/app/[locale]/(main)/ClientComponents/ServerDetailChartClient"; +import ServerDetailClient from "@/app/[locale]/(main)/ClientComponents/ServerDetailClient"; +import { Separator } from "@/components/ui/separator"; + +export default function Page({ params }: { params: { id: string } }) { + return ( +
+ + + +
+ ); +} diff --git a/app/[locale]/(main)/[id]/page.tsx b/app/[locale]/(main)/network/[id]/page.tsx similarity index 100% rename from app/[locale]/(main)/[id]/page.tsx rename to app/[locale]/(main)/network/[id]/page.tsx diff --git a/app/[locale]/types/nezha-api.ts b/app/[locale]/types/nezha-api.ts index ecfb986..899809a 100644 --- a/app/[locale]/types/nezha-api.ts +++ b/app/[locale]/types/nezha-api.ts @@ -1,7 +1,8 @@ export type ServerApi = { live_servers: number; offline_servers: number; - total_bandwidth: number; + total_out_bandwidth: number; + total_in_bandwidth: number; result: NezhaAPISafe[]; }; diff --git a/app/api/detail/route.ts b/app/api/detail/route.ts new file mode 100644 index 0000000..b0f8bc1 --- /dev/null +++ b/app/api/detail/route.ts @@ -0,0 +1,29 @@ +import { NezhaAPISafe } from "@/app/[locale]/types/nezha-api"; +import { GetServerDetail } from "@/lib/serverFetch"; +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +interface NezhaDataResponse { + error?: string; + data?: NezhaAPISafe; +} + +export async function GET(req: Request) { + const { searchParams } = new URL(req.url); + const server_id = searchParams.get("server_id"); + if (!server_id) { + return NextResponse.json( + { error: "server_id is required" }, + { status: 400 }, + ); + } + const response = (await GetServerDetail({ + server_id: parseInt(server_id), + })) as NezhaDataResponse; + if (response.error) { + console.log(response.error); + return NextResponse.json({ error: response.error }, { status: 400 }); + } + return NextResponse.json(response, { status: 200 }); +} diff --git a/bun.lockb b/bun.lockb index 1ab2b3f..35a64c1 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..6517a5c --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[install] +registry = "https://registry.npmmirror.com/" diff --git a/components/ServerCard.tsx b/components/ServerCard.tsx index a1a6ec7..0e15ebf 100644 --- a/components/ServerCard.tsx +++ b/components/ServerCard.tsx @@ -36,38 +36,34 @@ export default function ServerCard({ "flex flex-col items-center justify-start gap-3 p-3 md:px-5 lg:flex-row" } > - - -
- -
- {showFlag ? : null} -
-

- {name} -

-
-
- - - -
+
{ + router.push(`/${locale}/detail/${id}`); + }} + > + +
+ {showFlag ? : null} +
+

+ {name} +

+
{ - router.push(`/${locale}/${id}`); + router.push(`/${locale}/network/${id}`); }} className="flex flex-col gap-2 cursor-pointer" > @@ -109,7 +105,7 @@ export default function ServerCard({ {showNetTransfer && (
{ - router.push(`/${locale}/${id}`); + router.push(`/${locale}/network/${id}`); }} className={"flex items-center justify-between gap-1"} > diff --git a/components/ui/animated-circular-progress-bar.tsx b/components/ui/animated-circular-progress-bar.tsx new file mode 100644 index 0000000..dd96fe8 --- /dev/null +++ b/components/ui/animated-circular-progress-bar.tsx @@ -0,0 +1,107 @@ +import { cn } from "@/lib/utils"; + +interface Props { + max: number; + value: number; + min: number; + className?: string; + primaryColor?: string; +} + +export default function AnimatedCircularProgressBar({ + max = 100, + min = 0, + value = 0, + primaryColor, + className, +}: Props) { + const circumference = 2 * Math.PI * 45; + const percentPx = circumference / 100; + const currentPercent = ((value - min) / (max - min)) * 100; + + return ( +
+ + {currentPercent <= 90 && currentPercent >= 0 && ( + + )} + + + + {currentPercent} + +
+ ); +} diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index 2cba296..7c304ec 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -3,7 +3,7 @@ import { type VariantProps, cva } from "class-variance-authority"; import * as React from "react"; const badgeVariants = cva( - "inline-flex items-center text-nowarp rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + "inline-flex items-center text-nowarp rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors pointer-events-none focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", { variants: { variant: { diff --git a/lib/serverFetch.tsx b/lib/serverFetch.tsx index 5f3812c..480bd5a 100644 --- a/lib/serverFetch.tsx +++ b/lib/serverFetch.tsx @@ -36,7 +36,8 @@ export async function GetNezhaData() { const data: ServerApi = { live_servers: 0, offline_servers: 0, - total_bandwidth: 0, + total_out_bandwidth: 0, + total_in_bandwidth: 0, result: [], }; @@ -61,7 +62,8 @@ export async function GetNezhaData() { data.live_servers += 1; element.online_status = true; } - data.total_bandwidth += element.status.NetOutTransfer; + data.total_out_bandwidth += element.status.NetOutTransfer; + data.total_in_bandwidth += element.status.NetInTransfer; delete element.ipv4; delete element.ipv6; @@ -112,3 +114,55 @@ export async function GetServerMonitor({ server_id }: { server_id: number }) { return error; } } + +export async function GetServerDetail({ server_id }: { server_id: number }) { + var nezhaBaseUrl = getEnv("NezhaBaseUrl"); + if (!nezhaBaseUrl) { + console.log("NezhaBaseUrl is not set"); + return { error: "NezhaBaseUrl is not set" }; + } + + // Remove trailing slash + if (nezhaBaseUrl[nezhaBaseUrl.length - 1] === "/") { + nezhaBaseUrl = nezhaBaseUrl.slice(0, -1); + } + + try { + const response = await fetch( + nezhaBaseUrl + `/api/v1/server/details?id=${server_id}`, + { + headers: { + Authorization: getEnv("NezhaAuth") as string, + }, + next: { + revalidate: 0, + }, + }, + ); + const resData = await response.json(); + const detailDataList = resData.result; + if (!detailDataList) { + console.log(resData); + return { error: "MonitorData fetch failed" }; + } + + const timestamp = Date.now() / 1000; + const detailData = detailDataList.map( + (element: MakeOptional) => { + if (timestamp - element.last_active > 300) { + element.online_status = false; + } else { + element.online_status = true; + } + delete element.ipv4; + delete element.ipv6; + delete element.valid_ip; + return element; + }, + )[0]; + + return detailData; + } catch (error) { + return error; + } +} diff --git a/lib/utils.ts b/lib/utils.ts index a65328d..5dc9143 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -10,10 +10,15 @@ export function formatNezhaInfo(serverInfo: NezhaAPISafe) { return { ...serverInfo, cpu: serverInfo.status.CPU, + process: serverInfo.status.ProcessCount, up: serverInfo.status.NetOutSpeed / 1024 / 1024, down: serverInfo.status.NetInSpeed / 1024 / 1024, online: serverInfo.online_status, + tcp: serverInfo.status.TcpConnCount, + udp: serverInfo.status.UdpConnCount, mem: (serverInfo.status.MemUsed / serverInfo.host.MemTotal) * 100, + swap: (serverInfo.status.SwapUsed / serverInfo.host.SwapTotal) * 100, + disk: (serverInfo.status.DiskUsed / serverInfo.host.DiskTotal) * 100, stg: (serverInfo.status.DiskUsed / serverInfo.host.DiskTotal) * 100, country_code: serverInfo.host.CountryCode, }; diff --git a/messages/en.json b/messages/en.json index 3d09ae7..56e6d7d 100644 --- a/messages/en.json +++ b/messages/en.json @@ -35,6 +35,29 @@ "NetworkChart": { "ServerMonitorCount": "Services" }, + "ServerDetailClient": { + "detail_fetch_error_message": "Please check your environment variables and review the server console", + "status": "Status", + "Online": "Online", + "Offline": "Offline", + "Uptime": "Uptime", + "Days": "Days", + "Version": "Version", + "Arch": "Arch", + "Mem": "Mem", + "Disk": "Disk", + "System": "System", + "CPU": "CPU" + }, + "ServerDetailChartClient": { + "chart_fetch_error_message": "Please check your environment variables and review the server console", + "Process": "Process", + "Disk": "Disk", + "Mem": "Mem", + "Swap": "Swap", + "Upload": "Upload", + "Download": "Download" + }, "ThemeSwitcher": { "Light": "Light", "Dark": "Dark", diff --git a/messages/ja.json b/messages/ja.json index e308748..3f24abc 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -35,6 +35,29 @@ "NetworkChart": { "ServerMonitorCount": "サービス" }, + "ServerDetailClient": { + "detail_fetch_error_message": "環境変数を確認し、サーバーコンソールを確認してください", + "status": "ステータス", + "Online": "オンライン", + "Offline": "オフライン", + "Uptime": "稼働時間", + "Days": "日", + "Version": "バージョン", + "Arch": "アーキテクチャ", + "Mem": "メモリ", + "Disk": "ディスク", + "System": "システム", + "CPU": "CPU" + }, + "ServerDetailChartClient": { + "chart_fetch_error_message": "環境変数を確認し、サーバーコンソールを確認してください", + "Process": "進捗状況", + "Disk": "ディスク", + "Mem": "メモリ", + "Swap": "スワップ", + "Upload": "アップロード", + "Download": "ダウンロード" + }, "ThemeSwitcher": { "Light": "ライト", "Dark": "ダーク", diff --git a/messages/zh-t.json b/messages/zh-t.json index 9efe44c..50e43f6 100644 --- a/messages/zh-t.json +++ b/messages/zh-t.json @@ -35,6 +35,29 @@ "NetworkChart": { "ServerMonitorCount": "個監測服務" }, + "ServerDetailClient": { + "detail_fetch_error_message": "獲取伺服器詳情失敗,請檢查您的環境變數並檢查伺服器控制台", + "status": "狀態", + "Online": "在線", + "Offline": "離線", + "Uptime": "稼働時間", + "Days": "天", + "Version": "版本", + "Arch": "架構", + "Mem": "記憶體", + "Disk": "磁碟", + "System": "系統", + "CPU": "CPU" + }, + "ServerDetailChartClient": { + "chart_fetch_error_message": "獲取伺服器詳情失敗,請檢查您的環境變數並檢查伺服器控制台", + "Process": "進程", + "Disk": "磁碟", + "Mem": "記憶體", + "Swap": "虛擬記憶體", + "Upload": "上傳", + "Download": "下載" + }, "ThemeSwitcher": { "Light": "亮色", "Dark": "暗色", diff --git a/messages/zh.json b/messages/zh.json index c573923..2c770f5 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -35,6 +35,29 @@ "NetworkChart": { "ServerMonitorCount": "个监控服务" }, + "ServerDetailClient": { + "detail_fetch_error_message": "获取服务器详情失败,请检查您的环境变量并检查服务器控制台", + "status": "状态", + "Online": "在线", + "Offline": "离线", + "Uptime": "运行时间", + "Days": "天", + "Version": "版本", + "Arch": "架构", + "Mem": "内存", + "Disk": "磁盘", + "System": "系统", + "CPU": "CPU" + }, + "ServerDetailChartClient": { + "chart_fetch_error_message": "获取服务器详情失败,请检查您的环境变量并检查服务器控制台", + "Process": "进程", + "Disk": "磁盘", + "Mem": "内存", + "Swap": "虚拟内存", + "Upload": "上传", + "Download": "下载" + }, "ThemeSwitcher": { "Light": "亮色", "Dark": "暗色", diff --git a/prettier.config.js b/prettier.config.js index d9c7057..7d1ee54 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -3,6 +3,7 @@ module.exports = { importOrder: ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"], importOrderSeparation: true, importOrderSortSpecifiers: true, + endOfLine: "auto", plugins: [ "prettier-plugin-tailwindcss", "@trivago/prettier-plugin-sort-imports",