Merge branch 'main' into cloudflare

This commit is contained in:
hamster1963 2024-10-09 22:57:33 +08:00
commit 286111535f
12 changed files with 128 additions and 67 deletions

BIN
.github/shot-1.jpeg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

BIN
.github/shot-2.jpeg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

BIN
.github/shot-3.jpeg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

BIN
.github/shot-4.jpeg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

View File

@ -31,5 +31,7 @@
| 英语 | en | 是 |
| 日语 | ja | 是 |
![screen-shot-one](/.github/shotOne.png)
![screen-shot-two](/.github/shotTwo.png)
![screen-shot-one](/.github/shot-1.jpeg)
![screen-shot-three](/.github/shot-3.jpeg)
![screen-shot-two](/.github/shot-2.jpeg)
![screen-shot-four](/.github/shot-4.jpeg)

View File

@ -17,7 +17,7 @@ import {
ChartTooltipContent,
} from "@/components/ui/chart";
import useSWR from "swr";
import { ServerMonitorChart } from "../../types/nezha-api";
import { NezhaAPIMonitor, ServerMonitorChart } from "../../types/nezha-api";
import { formatTime, nezhaFetcher } from "@/lib/utils";
import { formatRelativeTime } from "@/lib/utils";
import { BackIcon } from "@/components/Icon";
@ -26,9 +26,14 @@ import { useLocale } from "next-intl";
import { useTranslations } from "next-intl";
import NetworkChartLoading from "./NetworkChartLoading";
interface ResultItem {
created_at: number;
[key: string]: number;
}
export function NetworkChartClient({ server_id }: { server_id: number }) {
const t = useTranslations("NetworkChartClient");
const { data, error } = useSWR<ServerMonitorChart>(
const { data, error } = useSWR<NezhaAPIMonitor[]>(
`/api/monitor?server_id=${server_id}`,
nezhaFetcher,
);
@ -41,19 +46,64 @@ export function NetworkChartClient({ server_id }: { server_id: number }) {
);
if (!data) return <NetworkChartLoading />;
function transformData(data: NezhaAPIMonitor[]) {
const monitorData: ServerMonitorChart = {};
data.forEach((item) => {
const monitorName = item.monitor_name;
if (!monitorData[monitorName]) {
monitorData[monitorName] = [];
}
for (let i = 0; i < item.created_at.length; i++) {
monitorData[monitorName].push({
created_at: item.created_at[i],
avg_delay: item.avg_delay[i],
});
}
});
return monitorData;
}
const formatData = (rawData: NezhaAPIMonitor[]) => {
const result: { [time: number]: ResultItem } = {};
// 遍历每个监控项
rawData.forEach((item) => {
const { monitor_name, created_at, avg_delay } = item;
created_at.forEach((time, index) => {
if (!result[time]) {
result[time] = { created_at: time };
}
result[time][monitor_name] = parseFloat(avg_delay[index].toFixed(2));
});
});
return Object.values(result).sort((a, b) => a.created_at - b.created_at);
};
const transformedData = transformData(data);
const formattedData = formatData(data);
const initChartConfig = {
avg_delay: {
label: t("avg_delay"),
},
} satisfies ChartConfig;
const chartDataKey = Object.keys(data);
const chartDataKey = Object.keys(transformedData);
return (
<NetworkChart
chartDataKey={chartDataKey}
chartConfig={initChartConfig}
chartData={data}
chartData={transformedData}
serverName={data[0].server_name}
formattedData={formattedData}
/>
);
}
@ -62,22 +112,34 @@ export function NetworkChart({
chartDataKey,
chartConfig,
chartData,
serverName,
formattedData,
}: {
chartDataKey: string[];
chartConfig: ChartConfig;
chartData: ServerMonitorChart;
serverName: string;
formattedData: ResultItem[];
}) {
const t = useTranslations("NetworkChart");
const router = useRouter();
const locale = useLocale();
const [activeChart, setActiveChart] = React.useState<
keyof typeof chartConfig
>(chartDataKey[0]);
const defaultChart = "All";
const [activeChart, setActiveChart] = React.useState(defaultChart);
const handleButtonClick = (chart: string) => {
if (chart === activeChart) {
setActiveChart(defaultChart);
} else {
setActiveChart(chart);
}
};
const getColorByIndex = (chart: string) => {
const index = chartDataKey.indexOf(chart);
return `hsl(var(--chart-${(index % 5) + 1}))`;
return `hsl(var(--chart-${(index % 10) + 1}))`;
};
return (
@ -91,24 +153,23 @@ export function NetworkChart({
className="flex flex-none cursor-pointer items-center gap-0.5 text-xl"
>
<BackIcon />
{chartData[chartDataKey[0]][0].server_name}
{serverName}
</CardTitle>
<CardDescription className="text-xs">
{chartDataKey.length} {t("ServerMonitorCount")}
</CardDescription>
</div>
<div className="flex flex-wrap">
{chartDataKey.map((key, index) => {
const chart = key as keyof typeof chartConfig;
{chartDataKey.map((key) => {
return (
<button
key={key}
data-active={activeChart === key}
className={`relative z-30 flex flex-1 flex-col justify-center gap-1 border-b px-6 py-4 text-left data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-6`}
onClick={() => setActiveChart(key as keyof typeof chartConfig)}
onClick={() => handleButtonClick(key)}
>
<span className="whitespace-nowrap text-xs text-muted-foreground">
{chart}
{key}
</span>
<span className="text-md font-bold leading-none sm:text-lg">
{chartData[key][chartData[key].length - 1].avg_delay.toFixed(
@ -128,7 +189,11 @@ export function NetworkChart({
>
<LineChart
accessibilityLayer
data={chartData[activeChart]}
data={
activeChart === defaultChart
? formattedData
: chartData[activeChart]
}
margin={{
left: 12,
right: 12,
@ -143,7 +208,6 @@ export function NetworkChart({
tickFormatter={(value) => formatRelativeTime(value)}
/>
<YAxis
dataKey="avg_delay"
tickLine={false}
axisLine={false}
mirror={true}
@ -155,8 +219,7 @@ export function NetworkChart({
content={
<ChartTooltipContent
indicator={"dot"}
className="w-fit"
nameKey="avg_delay"
className="gap-2"
labelKey="created_at"
labelClassName="text-muted-foreground"
labelFormatter={(_, payload) => {
@ -165,14 +228,28 @@ export function NetworkChart({
/>
}
/>
<Line
isAnimationActive={false}
strokeWidth={2}
type="linear"
dot={false}
dataKey="avg_delay"
stroke={getColorByIndex(activeChart)}
/>
{activeChart !== defaultChart && (
<Line
isAnimationActive={false}
strokeWidth={2}
type="linear"
dot={false}
dataKey="avg_delay"
stroke={getColorByIndex(activeChart)}
/>
)}
{activeChart === defaultChart &&
chartDataKey.map((key) => (
<Line
key={key}
isAnimationActive={false}
strokeWidth={2}
type="linear"
dot={false}
dataKey={key}
stroke={getColorByIndex(key)}
/>
))}
</LineChart>
</ChartContainer>
</CardContent>

View File

@ -58,7 +58,6 @@ export interface NezhaAPIStatus {
export type ServerMonitorChart = {
[key: string]: {
server_name: string;
created_at: number;
avg_delay: number;
}[];

BIN
bun.lockb

Binary file not shown.

View File

@ -239,7 +239,7 @@ const ChartTooltipContent = React.forwardRef<
</span>
</div>
{item.value && (
<span className="font-mono font-medium tabular-nums text-foreground">
<span className="ml-2 font-mono font-medium tabular-nums text-foreground">
{item.value.toLocaleString()}
</span>
)}

View File

@ -1,11 +1,6 @@
"use server";
import {
NezhaAPI,
NezhaAPIMonitor,
ServerApi,
ServerMonitorChart,
} from "../app/[locale]/types/nezha-api";
import { NezhaAPI, ServerApi } from "../app/[locale]/types/nezha-api";
import { MakeOptional } from "../app/[locale]/types/utils";
import { unstable_noStore as noStore } from "next/cache";
import getEnv from "./env-entry";
@ -71,28 +66,6 @@ export async function GetNezhaData() {
}
export async function GetServerMonitor({ server_id }: { server_id: number }) {
function transformData(data: NezhaAPIMonitor[]) {
const monitorData: ServerMonitorChart = {};
data.forEach((item) => {
const monitorName = item.monitor_name;
if (!monitorData[monitorName]) {
monitorData[monitorName] = [];
}
for (let i = 0; i < item.created_at.length; i++) {
monitorData[monitorName].push({
server_name: item.server_name,
created_at: item.created_at[i],
avg_delay: item.avg_delay[i],
});
}
});
return monitorData;
}
var nezhaBaseUrl = getEnv("NezhaBaseUrl");
if (!nezhaBaseUrl) {
console.log("NezhaBaseUrl is not set");
@ -122,7 +95,7 @@ export async function GetServerMonitor({ server_id }: { server_id: number }) {
console.log(resData);
return { error: "MonitorData fetch failed" };
}
return transformData(monitorData);
return monitorData;
} catch (error) {
return error;
}

View File

@ -28,11 +28,11 @@
"country-flag-icons": "^1.5.13",
"eslint-plugin-simple-import-sort": "^12.1.1",
"flag-icons": "^7.2.3",
"framer-motion": "^11.11.4",
"framer-motion": "^11.11.7",
"lucide-react": "^0.451.0",
"luxon": "^3.5.0",
"next": "^14.2.15",
"next-intl": "^3.20.0",
"next-intl": "^3.21.1",
"next-runtime-env": "^3.2.2",
"next-themes": "^0.3.0",
"react": "^18.3.1",

View File

@ -24,11 +24,16 @@
--input: 20 5.9% 90%;
--ring: 20 14.3% 4.1%;
--radius: 1rem;
--chart-1: 173 58% 39%;
--chart-2: 12 76% 61%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--chart-1: 220 70% 50%;
--chart-2: 340 75% 55%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 160 60% 45%;
--chart-6: 180 50% 50%;
--chart-7: 216 50% 50%;
--chart-8: 252 50% 50%;
--chart-9: 288 50% 50%;
--chart-10: 324 50% 50%;
}
.dark {
@ -52,10 +57,15 @@
--input: 12 6.5% 15.1%;
--ring: 24 5.7% 82.9%;
--chart-1: 220 70% 50%;
--chart-5: 160 60% 45%;
--chart-2: 340 75% 55%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-2: 340 75% 55%;
--chart-5: 160 60% 45%;
--chart-6: 180 50% 50%;
--chart-7: 216 50% 50%;
--chart-8: 252 50% 50%;
--chart-9: 288 50% 50%;
--chart-10: 324 50% 50%;
}
}