mirror of
https://github.com/hamster1963/nezha-dash.git
synced 2025-04-24 21:10:45 +08:00
refactor: data retrieval function
This commit is contained in:
parent
7cfc5a49cc
commit
a16dd290a7
@ -11,12 +11,11 @@ export default function ServerListClient() {
|
|||||||
});
|
});
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
const sortedResult = data.result.sort((a, b) => a.id - b.id);
|
const sortedResult = data.result.sort((a, b) => a.id - b.id);
|
||||||
const timestamp = Date.now() / 1000;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={"grid grid-cols-1 gap-2 md:grid-cols-2"}>
|
<section className={"grid grid-cols-1 gap-2 md:grid-cols-2"}>
|
||||||
{sortedResult.map((serverInfo) => (
|
{sortedResult.map((serverInfo) => (
|
||||||
<ServerCard key={serverInfo.id} timestamp={timestamp} serverInfo={serverInfo} />
|
<ServerCard key={serverInfo.id} serverInfo={serverInfo} />
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
@ -38,7 +38,7 @@ function Header() {
|
|||||||
|
|
||||||
// https://github.com/streamich/react-use/blob/master/src/useInterval.ts
|
// https://github.com/streamich/react-use/blob/master/src/useInterval.ts
|
||||||
const useInterval = (callback: Function, delay?: number | null) => {
|
const useInterval = (callback: Function, delay?: number | null) => {
|
||||||
const savedCallback = useRef<Function>(() => { });
|
const savedCallback = useRef<Function>(() => {});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
savedCallback.current = callback;
|
savedCallback.current = callback;
|
||||||
@ -61,7 +61,9 @@ function Overview() {
|
|||||||
}, []);
|
}, []);
|
||||||
const timeOption = DateTime.TIME_SIMPLE;
|
const timeOption = DateTime.TIME_SIMPLE;
|
||||||
timeOption.hour12 = true;
|
timeOption.hour12 = true;
|
||||||
const [timeString, setTimeString] = useState(DateTime.now().setLocale("en-US").toLocaleString(timeOption));
|
const [timeString, setTimeString] = useState(
|
||||||
|
DateTime.now().setLocale("en-US").toLocaleString(timeOption),
|
||||||
|
);
|
||||||
|
|
||||||
useInterval(() => {
|
useInterval(() => {
|
||||||
setTimeString(DateTime.now().setLocale("en-US").toLocaleString(timeOption));
|
setTimeString(DateTime.now().setLocale("en-US").toLocaleString(timeOption));
|
||||||
@ -73,9 +75,7 @@ function Overview() {
|
|||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<p className="text-sm font-medium opacity-50">where the time is</p>
|
<p className="text-sm font-medium opacity-50">where the time is</p>
|
||||||
{mouted && (
|
{mouted && (
|
||||||
<p className="opacity-1 text-sm font-medium">
|
<p className="opacity-1 text-sm font-medium">{timeString}</p>
|
||||||
{timeString}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import ServerList from "@/components/ServerList";
|
import ServerList from "@/components/ServerList";
|
||||||
import ServerOverview from "@/components/ServerOverview";
|
import ServerOverview from "@/components/ServerOverview";
|
||||||
import { GetNezhaData } from "@/lib/prefetch";
|
import { GetNezhaData } from "@/lib/serverFetch";
|
||||||
|
|
||||||
import { SWRConfig } from "swr";
|
import { SWRConfig } from "swr";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
@ -1,57 +1,10 @@
|
|||||||
import { NezhaAPI, ServerApi } from "@/app/types/nezha-api";
|
import { GetNezhaData } from "@/lib/serverFetch";
|
||||||
import { MakeOptional } from "@/app/types/utils";
|
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
export async function GET(_: Request) {
|
export async function GET(_: Request) {
|
||||||
if (!process.env.NezhaBaseUrl) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "NezhaBaseUrl is not set" },
|
|
||||||
{ status: 400 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove trailing slash
|
|
||||||
var nezhaBaseUrl = process.env.NezhaBaseUrl;
|
|
||||||
|
|
||||||
if (process.env.NezhaBaseUrl[process.env.NezhaBaseUrl.length - 1] === "/") {
|
|
||||||
nezhaBaseUrl = process.env.NezhaBaseUrl.slice(0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(nezhaBaseUrl + "/api/v1/server/details", {
|
const response = await GetNezhaData();
|
||||||
headers: {
|
return NextResponse.json(response, { status: 200 });
|
||||||
Authorization: process.env.NezhaAuth as string,
|
|
||||||
},
|
|
||||||
next: {
|
|
||||||
revalidate: 0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const nezhaData = (await response.json()).result as NezhaAPI[];
|
|
||||||
const data: ServerApi = {
|
|
||||||
live_servers: 0,
|
|
||||||
offline_servers: 0,
|
|
||||||
total_bandwidth: 0,
|
|
||||||
result: [],
|
|
||||||
};
|
|
||||||
const timestamp = Date.now() / 1000;
|
|
||||||
data.result = nezhaData.map(
|
|
||||||
(element: MakeOptional<NezhaAPI, "ipv4" | "ipv6" | "valid_ip">) => {
|
|
||||||
if (timestamp - element.last_active > 300) {
|
|
||||||
data.offline_servers += 1;
|
|
||||||
} else {
|
|
||||||
data.live_servers += 1;
|
|
||||||
}
|
|
||||||
data.total_bandwidth += element.status.NetOutTransfer;
|
|
||||||
|
|
||||||
delete element.ipv4;
|
|
||||||
delete element.ipv6;
|
|
||||||
delete element.valid_ip;
|
|
||||||
|
|
||||||
return element;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return NextResponse.json(data, { status: 200 });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return NextResponse.json({ error: error }, { status: 200 });
|
return NextResponse.json({ error: error }, { status: 200 });
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ export interface NezhaAPI {
|
|||||||
name: string;
|
name: string;
|
||||||
tag: string;
|
tag: string;
|
||||||
last_active: number;
|
last_active: number;
|
||||||
|
online_status: boolean;
|
||||||
ipv4: string;
|
ipv4: string;
|
||||||
ipv6: string;
|
ipv6: string;
|
||||||
valid_ip: string;
|
valid_ip: string;
|
||||||
|
@ -10,18 +10,14 @@ import { formatNezhaInfo } from "@/lib/utils";
|
|||||||
import ServerCardPopover from "./ServerCardPopover";
|
import ServerCardPopover from "./ServerCardPopover";
|
||||||
|
|
||||||
export default function ServerCard({
|
export default function ServerCard({
|
||||||
timestamp,
|
|
||||||
serverInfo,
|
serverInfo,
|
||||||
}: {
|
}: {
|
||||||
timestamp: number;
|
|
||||||
serverInfo: NezhaAPISafe;
|
serverInfo: NezhaAPISafe;
|
||||||
}) {
|
}) {
|
||||||
const { name, online, cpu, up, down, mem, stg, ...props } = formatNezhaInfo(
|
const { name, online, cpu, up, down, mem, stg, ...props } =
|
||||||
timestamp,
|
formatNezhaInfo(serverInfo);
|
||||||
serverInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
return online === "online" ? (
|
return online ? (
|
||||||
<Card
|
<Card
|
||||||
className={
|
className={
|
||||||
"flex flex-col items-center justify-start gap-3 p-3 md:px-5 lg:flex-row"
|
"flex flex-col items-center justify-start gap-3 p-3 md:px-5 lg:flex-row"
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const Popover = PopoverPrimitive.Root
|
const Popover = PopoverPrimitive.Root;
|
||||||
|
|
||||||
const PopoverTrigger = PopoverPrimitive.Trigger
|
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||||
|
|
||||||
const PopoverContent = React.forwardRef<
|
const PopoverContent = React.forwardRef<
|
||||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||||
@ -20,12 +20,12 @@ const PopoverContent = React.forwardRef<
|
|||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</PopoverPrimitive.Portal>
|
</PopoverPrimitive.Portal>
|
||||||
))
|
));
|
||||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName
|
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Popover, PopoverTrigger, PopoverContent }
|
export { Popover, PopoverTrigger, PopoverContent };
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
import { NezhaAPI, ServerApi } from "@/app/types/nezha-api";
|
|
||||||
import { MakeOptional } from "@/app/types/utils";
|
|
||||||
import { error } from "console";
|
|
||||||
|
|
||||||
export async function GetNezhaData() {
|
|
||||||
if (!process.env.NezhaBaseUrl) {
|
|
||||||
error("NezhaBaseUrl is not set");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Remove trailing slash
|
|
||||||
var nezhaBaseUrl = process.env.NezhaBaseUrl;
|
|
||||||
|
|
||||||
if (process.env.NezhaBaseUrl[process.env.NezhaBaseUrl.length - 1] === "/") {
|
|
||||||
nezhaBaseUrl = process.env.NezhaBaseUrl.slice(0, -1);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const response = await fetch(nezhaBaseUrl + "/api/v1/server/details", {
|
|
||||||
headers: {
|
|
||||||
Authorization: process.env.NezhaAuth as string,
|
|
||||||
},
|
|
||||||
next: {
|
|
||||||
revalidate: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const nezhaData = (await response.json()).result as NezhaAPI[];
|
|
||||||
const data: ServerApi = {
|
|
||||||
live_servers: 0,
|
|
||||||
offline_servers: 0,
|
|
||||||
total_bandwidth: 0,
|
|
||||||
result: [],
|
|
||||||
};
|
|
||||||
const timestamp = Date.now() / 1000;
|
|
||||||
data.result = nezhaData.map(
|
|
||||||
(element: MakeOptional<NezhaAPI, "ipv4" | "ipv6" | "valid_ip">) => {
|
|
||||||
if (timestamp - element.last_active > 300) {
|
|
||||||
data.offline_servers += 1;
|
|
||||||
} else {
|
|
||||||
data.live_servers += 1;
|
|
||||||
}
|
|
||||||
data.total_bandwidth += element.status.NetOutTransfer;
|
|
||||||
|
|
||||||
delete element.ipv4;
|
|
||||||
delete element.ipv6;
|
|
||||||
delete element.valid_ip;
|
|
||||||
|
|
||||||
return element;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
} catch (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
}
|
|
58
lib/serverFetch.tsx
Normal file
58
lib/serverFetch.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { NezhaAPI, ServerApi } from "@/app/types/nezha-api";
|
||||||
|
import { MakeOptional } from "@/app/types/utils";
|
||||||
|
import { error } from "console";
|
||||||
|
|
||||||
|
export async function GetNezhaData() {
|
||||||
|
if (!process.env.NezhaBaseUrl) {
|
||||||
|
error("NezhaBaseUrl is not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Remove trailing slash
|
||||||
|
var nezhaBaseUrl = process.env.NezhaBaseUrl;
|
||||||
|
|
||||||
|
if (process.env.NezhaBaseUrl[process.env.NezhaBaseUrl.length - 1] === "/") {
|
||||||
|
nezhaBaseUrl = process.env.NezhaBaseUrl.slice(0, -1);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetch(nezhaBaseUrl + "/api/v1/server/details", {
|
||||||
|
headers: {
|
||||||
|
Authorization: process.env.NezhaAuth as string,
|
||||||
|
},
|
||||||
|
next: {
|
||||||
|
revalidate: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const nezhaData = (await response.json()).result as NezhaAPI[];
|
||||||
|
const data: ServerApi = {
|
||||||
|
live_servers: 0,
|
||||||
|
offline_servers: 0,
|
||||||
|
total_bandwidth: 0,
|
||||||
|
result: [],
|
||||||
|
};
|
||||||
|
const timestamp = Date.now() / 1000;
|
||||||
|
data.result = nezhaData.map(
|
||||||
|
(element: MakeOptional<NezhaAPI, "ipv4" | "ipv6" | "valid_ip">) => {
|
||||||
|
if (timestamp - element.last_active > 300) {
|
||||||
|
data.offline_servers += 1;
|
||||||
|
element.online_status = false;
|
||||||
|
} else {
|
||||||
|
data.live_servers += 1;
|
||||||
|
element.online_status = true;
|
||||||
|
}
|
||||||
|
data.total_bandwidth += element.status.NetOutTransfer;
|
||||||
|
|
||||||
|
delete element.ipv4;
|
||||||
|
delete element.ipv6;
|
||||||
|
delete element.valid_ip;
|
||||||
|
|
||||||
|
return element;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
20
lib/utils.ts
20
lib/utils.ts
@ -6,16 +6,16 @@ export function cn(...inputs: ClassValue[]) {
|
|||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatNezhaInfo(timestamp: number, serverInfo: NezhaAPISafe) {
|
export function formatNezhaInfo(serverInfo: NezhaAPISafe) {
|
||||||
return {
|
return {
|
||||||
...serverInfo,
|
...serverInfo,
|
||||||
cpu: serverInfo.status.CPU,
|
cpu: serverInfo.status.CPU,
|
||||||
up: serverInfo.status.NetOutSpeed / 1024 / 1024,
|
up: serverInfo.status.NetOutSpeed / 1024 / 1024,
|
||||||
down: serverInfo.status.NetInSpeed / 1024 / 1024,
|
down: serverInfo.status.NetInSpeed / 1024 / 1024,
|
||||||
online: timestamp - serverInfo.last_active > 300 ? "offline" : "online",
|
online: serverInfo.online_status,
|
||||||
mem: (serverInfo.status.MemUsed / serverInfo.host.MemTotal) * 100,
|
mem: (serverInfo.status.MemUsed / serverInfo.host.MemTotal) * 100,
|
||||||
stg: (serverInfo.status.DiskUsed / serverInfo.host.DiskTotal) * 100,
|
stg: (serverInfo.status.DiskUsed / serverInfo.host.DiskTotal) * 100,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatBytes(bytes: number, decimals: number = 2) {
|
export function formatBytes(bytes: number, decimals: number = 2) {
|
||||||
|
Loading…
Reference in New Issue
Block a user