mirror of
https://github.com/hamster1963/nezha-dash.git
synced 2025-04-24 21:10:45 +08:00
refactor: biome config
This commit is contained in:
parent
a8f4c8564f
commit
646354e515
@ -1,10 +1,10 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { NezhaAPIMonitor, ServerMonitorChart } from "@/app/types/nezha-api"
|
import type { NezhaAPIMonitor, ServerMonitorChart } from "@/app/types/nezha-api"
|
||||||
import NetworkChartLoading from "@/components/loading/NetworkChartLoading"
|
import NetworkChartLoading from "@/components/loading/NetworkChartLoading"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import {
|
import {
|
||||||
ChartConfig,
|
type ChartConfig,
|
||||||
ChartContainer,
|
ChartContainer,
|
||||||
ChartLegend,
|
ChartLegend,
|
||||||
ChartLegendContent,
|
ChartLegendContent,
|
||||||
@ -120,9 +120,12 @@ export const NetworkChart = React.memo(function NetworkChart({
|
|||||||
() =>
|
() =>
|
||||||
chartDataKey.map((key) => (
|
chartDataKey.map((key) => (
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
key={key}
|
key={key}
|
||||||
data-active={activeChart === key}
|
data-active={activeChart === key}
|
||||||
className={`relative z-30 flex cursor-pointer grow basis-0 flex-col justify-center gap-1 border-b border-neutral-200 dark:border-neutral-800 px-6 py-4 text-left data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-6`}
|
className={
|
||||||
|
"relative z-30 flex cursor-pointer grow basis-0 flex-col justify-center gap-1 border-b border-neutral-200 dark:border-neutral-800 px-6 py-4 text-left data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-6"
|
||||||
|
}
|
||||||
onClick={() => handleButtonClick(key)}
|
onClick={() => handleButtonClick(key)}
|
||||||
>
|
>
|
||||||
<span className="whitespace-nowrap text-xs text-muted-foreground">{key}</span>
|
<span className="whitespace-nowrap text-xs text-muted-foreground">{key}</span>
|
||||||
@ -216,7 +219,7 @@ export const NetworkChart = React.memo(function NetworkChart({
|
|||||||
const smoothed = { ...point } as ResultItem
|
const smoothed = { ...point } as ResultItem
|
||||||
|
|
||||||
if (activeChart === defaultChart) {
|
if (activeChart === defaultChart) {
|
||||||
chartDataKey.forEach((key) => {
|
for (const key of chartDataKey) {
|
||||||
const values = window
|
const values = window
|
||||||
.map((w) => w[key])
|
.map((w) => w[key])
|
||||||
.filter((v) => v !== undefined && v !== null) as number[]
|
.filter((v) => v !== undefined && v !== null) as number[]
|
||||||
@ -233,7 +236,7 @@ export const NetworkChart = React.memo(function NetworkChart({
|
|||||||
smoothed[key] = ewmaHistory[key]
|
smoothed[key] = ewmaHistory[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
} else {
|
} else {
|
||||||
const values = window
|
const values = window
|
||||||
.map((w) => w.avg_delay)
|
.map((w) => w.avg_delay)
|
||||||
@ -243,12 +246,12 @@ export const NetworkChart = React.memo(function NetworkChart({
|
|||||||
const processed = processValues(values)
|
const processed = processValues(values)
|
||||||
if (processed !== null) {
|
if (processed !== null) {
|
||||||
// 应用EWMA平滑
|
// 应用EWMA平滑
|
||||||
if (ewmaHistory["current"] === undefined) {
|
if (ewmaHistory.current === undefined) {
|
||||||
ewmaHistory["current"] = processed
|
ewmaHistory.current = processed
|
||||||
} else {
|
} else {
|
||||||
ewmaHistory["current"] = alpha * processed + (1 - alpha) * ewmaHistory["current"]
|
ewmaHistory.current = alpha * processed + (1 - alpha) * ewmaHistory.current
|
||||||
}
|
}
|
||||||
smoothed.avg_delay = ewmaHistory["current"]
|
smoothed.avg_delay = ewmaHistory.current
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,7 +323,7 @@ export const NetworkChart = React.memo(function NetworkChart({
|
|||||||
const transformData = (data: NezhaAPIMonitor[]) => {
|
const transformData = (data: NezhaAPIMonitor[]) => {
|
||||||
const monitorData: ServerMonitorChart = {}
|
const monitorData: ServerMonitorChart = {}
|
||||||
|
|
||||||
data.forEach((item) => {
|
for (const item of data) {
|
||||||
const monitorName = item.monitor_name
|
const monitorName = item.monitor_name
|
||||||
|
|
||||||
if (!monitorData[monitorName]) {
|
if (!monitorData[monitorName]) {
|
||||||
@ -333,7 +336,7 @@ const transformData = (data: NezhaAPIMonitor[]) => {
|
|||||||
avg_delay: item.avg_delay[i],
|
avg_delay: item.avg_delay[i],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
return monitorData
|
return monitorData
|
||||||
}
|
}
|
||||||
@ -342,16 +345,18 @@ const formatData = (rawData: NezhaAPIMonitor[]) => {
|
|||||||
const result: { [time: number]: ResultItem } = {}
|
const result: { [time: number]: ResultItem } = {}
|
||||||
|
|
||||||
const allTimes = new Set<number>()
|
const allTimes = new Set<number>()
|
||||||
rawData.forEach((item) => {
|
for (const item of rawData) {
|
||||||
item.created_at.forEach((time) => allTimes.add(time))
|
for (const time of item.created_at) {
|
||||||
})
|
allTimes.add(time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const allTimeArray = Array.from(allTimes).sort((a, b) => a - b)
|
const allTimeArray = Array.from(allTimes).sort((a, b) => a - b)
|
||||||
|
|
||||||
rawData.forEach((item) => {
|
for (const item of rawData) {
|
||||||
const { monitor_name, created_at, avg_delay } = item
|
const { monitor_name, created_at, avg_delay } = item
|
||||||
|
|
||||||
allTimeArray.forEach((time) => {
|
for (const time of allTimeArray) {
|
||||||
if (!result[time]) {
|
if (!result[time]) {
|
||||||
result[time] = { created_at: time }
|
result[time] = { created_at: time }
|
||||||
}
|
}
|
||||||
@ -359,8 +364,8 @@ const formatData = (rawData: NezhaAPIMonitor[]) => {
|
|||||||
const timeIndex = created_at.indexOf(time)
|
const timeIndex = created_at.indexOf(time)
|
||||||
// @ts-expect-error - avg_delay is an array
|
// @ts-expect-error - avg_delay is an array
|
||||||
result[time][monitor_name] = timeIndex !== -1 ? avg_delay[timeIndex] : null
|
result[time][monitor_name] = timeIndex !== -1 ? avg_delay[timeIndex] : null
|
||||||
})
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
return Object.values(result).sort((a, b) => a.created_at - b.created_at)
|
return Object.values(result).sort((a, b) => a.created_at - b.created_at)
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
MAX_HISTORY_LENGTH,
|
MAX_HISTORY_LENGTH,
|
||||||
ServerDataWithTimestamp,
|
type ServerDataWithTimestamp,
|
||||||
useServerData,
|
useServerData,
|
||||||
} from "@/app/context/server-data-context"
|
} from "@/app/context/server-data-context"
|
||||||
import { NezhaAPISafe } from "@/app/types/nezha-api"
|
import type { 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"
|
||||||
import { Card, CardContent } from "@/components/ui/card"
|
import { Card, CardContent } from "@/components/ui/card"
|
||||||
import { ChartConfig, ChartContainer } from "@/components/ui/chart"
|
import { type 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, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
@ -684,14 +684,14 @@ function NetworkChart({
|
|||||||
<div className="flex flex-col w-20">
|
<div className="flex flex-col w-20">
|
||||||
<p className="text-xs text-muted-foreground">{t("Upload")}</p>
|
<p className="text-xs text-muted-foreground">{t("Upload")}</p>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-1))]"></span>
|
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-1))]" />
|
||||||
<p className="text-xs font-medium">{up.toFixed(2)} M/s</p>
|
<p className="text-xs font-medium">{up.toFixed(2)} M/s</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col w-20">
|
<div className="flex flex-col w-20">
|
||||||
<p className=" text-xs text-muted-foreground">{t("Download")}</p>
|
<p className=" text-xs text-muted-foreground">{t("Download")}</p>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-4))]"></span>
|
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-4))]" />
|
||||||
<p className="text-xs font-medium">{down.toFixed(2)} M/s</p>
|
<p className="text-xs font-medium">{down.toFixed(2)} M/s</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -826,14 +826,14 @@ function ConnectChart({
|
|||||||
<div className="flex flex-col w-12">
|
<div className="flex flex-col w-12">
|
||||||
<p className="text-xs text-muted-foreground">TCP</p>
|
<p className="text-xs text-muted-foreground">TCP</p>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-1))]"></span>
|
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-1))]" />
|
||||||
<p className="text-xs font-medium">{tcp}</p>
|
<p className="text-xs font-medium">{tcp}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col w-12">
|
<div className="flex flex-col w-12">
|
||||||
<p className=" text-xs text-muted-foreground">UDP</p>
|
<p className=" text-xs text-muted-foreground">UDP</p>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-4))]"></span>
|
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-4))]" />
|
||||||
<p className="text-xs font-medium">{udp}</p>
|
<p className="text-xs font-medium">{udp}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { IPInfo } from "@/app/api/server-ip/route"
|
import type { IPInfo } from "@/app/api/server-ip/route"
|
||||||
import { Loader } from "@/components/loading/Loader"
|
import { Loader } from "@/components/loading/Loader"
|
||||||
import { Card, CardContent } from "@/components/ui/card"
|
import { Card, CardContent } from "@/components/ui/card"
|
||||||
import { nezhaFetcher } from "@/lib/utils"
|
import { nezhaFetcher } from "@/lib/utils"
|
||||||
|
@ -13,6 +13,7 @@ export default function Footer() {
|
|||||||
href="https://github.com/hamster1963/nezha-dash"
|
href="https://github.com/hamster1963/nezha-dash"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="cursor-pointer font-normal underline decoration-yellow-500 hover:decoration-yellow-600 transition-colors decoration-2 underline-offset-2 dark:decoration-yellow-500/60 dark:hover:decoration-yellow-500/80"
|
className="cursor-pointer font-normal underline decoration-yellow-500 hover:decoration-yellow-600 transition-colors decoration-2 underline-offset-2 dark:decoration-yellow-500/60 dark:hover:decoration-yellow-500/80"
|
||||||
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
{t("a_303-585_GitHub")}
|
{t("a_303-585_GitHub")}
|
||||||
</a>
|
</a>
|
||||||
@ -20,6 +21,7 @@ export default function Footer() {
|
|||||||
href={`https://github.com/hamster1963/nezha-dash/releases/tag/v${version}`}
|
href={`https://github.com/hamster1963/nezha-dash/releases/tag/v${version}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="cursor-pointer font-normal underline decoration-yellow-500 hover:decoration-yellow-600 transition-colors decoration-2 underline-offset-2 dark:decoration-yellow-500/60 dark:hover:decoration-yellow-500/80"
|
className="cursor-pointer font-normal underline decoration-yellow-500 hover:decoration-yellow-600 transition-colors decoration-2 underline-offset-2 dark:decoration-yellow-500/60 dark:hover:decoration-yellow-500/80"
|
||||||
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
v{version}
|
v{version}
|
||||||
</a>
|
</a>
|
||||||
|
@ -24,7 +24,7 @@ function Header() {
|
|||||||
<section
|
<section
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
sessionStorage.removeItem("selectedTag")
|
sessionStorage.removeItem("selectedTag")
|
||||||
router.push(`/`)
|
router.push("/")
|
||||||
}}
|
}}
|
||||||
className="flex cursor-pointer items-center text-base font-medium hover:opacity-50 transition-opacity duration-300"
|
className="flex cursor-pointer items-center text-base font-medium hover:opacity-50 transition-opacity duration-300"
|
||||||
>
|
>
|
||||||
@ -80,10 +80,10 @@ function Links() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{links.map((link, index) => {
|
{links.map((link) => {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
key={index}
|
key={link.link}
|
||||||
href={link.link}
|
href={link.link}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@ -124,7 +124,7 @@ function Overview() {
|
|||||||
{mouted ? (
|
{mouted ? (
|
||||||
<p className="text-sm font-medium">{timeString}</p>
|
<p className="text-sm font-medium">{timeString}</p>
|
||||||
) : (
|
) : (
|
||||||
<Skeleton className="h-[20px] w-[50px] rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
<Skeleton className="h-[20px] w-[50px] rounded-[5px] bg-muted-foreground/10 animate-none" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -4,7 +4,7 @@ import { ServerDataProvider } from "@/app/context/server-data-context"
|
|||||||
import { auth } from "@/auth"
|
import { auth } from "@/auth"
|
||||||
import { SignIn } from "@/components/SignIn"
|
import { SignIn } from "@/components/SignIn"
|
||||||
import getEnv from "@/lib/env-entry"
|
import getEnv from "@/lib/env-entry"
|
||||||
import React from "react"
|
import type React from "react"
|
||||||
|
|
||||||
type DashboardProps = {
|
type DashboardProps = {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
|
@ -2,7 +2,7 @@ import { auth } from "@/auth"
|
|||||||
import getEnv from "@/lib/env-entry"
|
import getEnv from "@/lib/env-entry"
|
||||||
import { GetServerDetail } from "@/lib/serverFetch"
|
import { GetServerDetail } from "@/lib/serverFetch"
|
||||||
import { redirect } from "next/navigation"
|
import { redirect } from "next/navigation"
|
||||||
import { NextRequest, NextResponse } from "next/server"
|
import { type NextRequest, NextResponse } from "next/server"
|
||||||
|
|
||||||
export const dynamic = "force-dynamic"
|
export const dynamic = "force-dynamic"
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export async function GET(req: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const serverIdNum = parseInt(server_id, 10)
|
const serverIdNum = Number.parseInt(server_id, 10)
|
||||||
if (isNaN(serverIdNum)) {
|
if (isNaN(serverIdNum)) {
|
||||||
return NextResponse.json({ error: "server_id must be a valid number" }, { status: 400 })
|
return NextResponse.json({ error: "server_id must be a valid number" }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { auth } from "@/auth"
|
|||||||
import getEnv from "@/lib/env-entry"
|
import getEnv from "@/lib/env-entry"
|
||||||
import { GetServerMonitor } from "@/lib/serverFetch"
|
import { GetServerMonitor } from "@/lib/serverFetch"
|
||||||
import { redirect } from "next/navigation"
|
import { redirect } from "next/navigation"
|
||||||
import { NextRequest, NextResponse } from "next/server"
|
import { type NextRequest, NextResponse } from "next/server"
|
||||||
|
|
||||||
export const dynamic = "force-dynamic"
|
export const dynamic = "force-dynamic"
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export async function GET(req: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const serverIdNum = parseInt(server_id, 10)
|
const serverIdNum = Number.parseInt(server_id, 10)
|
||||||
if (isNaN(serverIdNum)) {
|
if (isNaN(serverIdNum)) {
|
||||||
return NextResponse.json({ error: "server_id must be a number" }, { status: 400 })
|
return NextResponse.json({ error: "server_id must be a number" }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import fs from "fs"
|
import fs from "node:fs"
|
||||||
import path from "path"
|
import path from "node:path"
|
||||||
import { auth } from "@/auth"
|
import { auth } from "@/auth"
|
||||||
import getEnv from "@/lib/env-entry"
|
import getEnv from "@/lib/env-entry"
|
||||||
import { GetServerIP } from "@/lib/serverFetch"
|
import { GetServerIP } from "@/lib/serverFetch"
|
||||||
import { AsnResponse, CityResponse, Reader } from "maxmind"
|
import { type AsnResponse, type CityResponse, Reader } from "maxmind"
|
||||||
import { redirect } from "next/navigation"
|
import { redirect } from "next/navigation"
|
||||||
import { NextRequest, NextResponse } from "next/server"
|
import { type NextRequest, NextResponse } from "next/server"
|
||||||
|
|
||||||
export const dynamic = "force-dynamic"
|
export const dynamic = "force-dynamic"
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { ReactNode, createContext, useContext, useState } from "react"
|
import { type ReactNode, createContext, useContext, useState } from "react"
|
||||||
|
|
||||||
interface FilterContextType {
|
interface FilterContextType {
|
||||||
filter: boolean
|
filter: boolean
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { ServerApi } from "@/app/types/nezha-api"
|
import type { ServerApi } from "@/app/types/nezha-api"
|
||||||
import getEnv from "@/lib/env-entry"
|
import getEnv from "@/lib/env-entry"
|
||||||
import { nezhaFetcher } from "@/lib/utils"
|
import { nezhaFetcher } from "@/lib/utils"
|
||||||
import { ReactNode, createContext, useContext, useEffect, useState } from "react"
|
import { type ReactNode, createContext, useContext, useEffect, useState } from "react"
|
||||||
import useSWR from "swr"
|
import useSWR from "swr"
|
||||||
|
|
||||||
export interface ServerDataWithTimestamp {
|
export interface ServerDataWithTimestamp {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { ReactNode, createContext, useContext, useState } from "react"
|
import { type ReactNode, createContext, useContext, useState } from "react"
|
||||||
|
|
||||||
type Status = "all" | "online" | "offline"
|
type Status = "all" | "online" | "offline"
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { ReactNode, createContext, useContext, useState } from "react"
|
import { type ReactNode, createContext, useContext, useState } from "react"
|
||||||
|
|
||||||
export interface TooltipData {
|
export interface TooltipData {
|
||||||
centroid: [number, number]
|
centroid: [number, number]
|
||||||
|
@ -6,13 +6,13 @@ import getEnv from "@/lib/env-entry"
|
|||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import "@/styles/globals.css"
|
import "@/styles/globals.css"
|
||||||
import type { Metadata } from "next"
|
import type { Metadata } from "next"
|
||||||
import { Viewport } from "next"
|
import type { Viewport } from "next"
|
||||||
import { NextIntlClientProvider } from "next-intl"
|
import { NextIntlClientProvider } from "next-intl"
|
||||||
import { getLocale, getMessages } from "next-intl/server"
|
import { getLocale, getMessages } from "next-intl/server"
|
||||||
import { PublicEnvScript } from "next-runtime-env"
|
import { PublicEnvScript } from "next-runtime-env"
|
||||||
import { ThemeProvider } from "next-themes"
|
import { ThemeProvider } from "next-themes"
|
||||||
import { Inter as FontSans } from "next/font/google"
|
import { Inter as FontSans } from "next/font/google"
|
||||||
import React from "react"
|
import type React from "react"
|
||||||
|
|
||||||
const fontSans = FontSans({
|
const fontSans = FontSans({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
@ -33,8 +33,8 @@ export const metadata: Metadata = {
|
|||||||
statusBarStyle: "default",
|
statusBarStyle: "default",
|
||||||
},
|
},
|
||||||
robots: {
|
robots: {
|
||||||
index: disableIndex ? false : true,
|
index: !disableIndex,
|
||||||
follow: disableIndex ? false : true,
|
follow: !disableIndex,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
biome.json
16
biome.json
@ -16,7 +16,10 @@
|
|||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": false,
|
"recommended": true,
|
||||||
|
"a11y": {
|
||||||
|
"useKeyWithClickEvents": "off"
|
||||||
|
},
|
||||||
"complexity": { "noUselessTypeConstraint": "error" },
|
"complexity": { "noUselessTypeConstraint": "error" },
|
||||||
"correctness": {
|
"correctness": {
|
||||||
"noUnusedVariables": "error",
|
"noUnusedVariables": "error",
|
||||||
@ -52,16 +55,7 @@
|
|||||||
"linter": {
|
"linter": {
|
||||||
"rules": {
|
"rules": {
|
||||||
"correctness": {
|
"correctness": {
|
||||||
"noUnusedImports": "error",
|
"noUnusedImports": "error"
|
||||||
"noConstAssign": "off",
|
|
||||||
"noGlobalObjectCalls": "off",
|
|
||||||
"noInvalidBuiltinInstantiation": "off",
|
|
||||||
"noInvalidConstructorSuper": "off",
|
|
||||||
"noNewSymbol": "off",
|
|
||||||
"noSetterReturn": "off",
|
|
||||||
"noUndeclaredVariables": "off",
|
|
||||||
"noUnreachable": "off",
|
|
||||||
"noUnreachableSuper": "off"
|
|
||||||
},
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"noArguments": "error",
|
"noArguments": "error",
|
||||||
|
@ -2,7 +2,7 @@ import Image from "next/image"
|
|||||||
|
|
||||||
export function GitHubIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
export function GitHubIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
||||||
return (
|
return (
|
||||||
<svg viewBox="0 0 496 512" fill="white" {...props}>
|
<svg role="img" aria-label="github-icon" viewBox="0 0 496 512" fill="white" {...props}>
|
||||||
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
|
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NezhaAPISafe } from "@/app/types/nezha-api"
|
import type { NezhaAPISafe } from "@/app/types/nezha-api"
|
||||||
import ServerFlag from "@/components/ServerFlag"
|
import ServerFlag from "@/components/ServerFlag"
|
||||||
import ServerUsageBar from "@/components/ServerUsageBar"
|
import ServerUsageBar from "@/components/ServerUsageBar"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
@ -43,7 +43,7 @@ export default function ServerCard({
|
|||||||
})}
|
})}
|
||||||
style={{ gridTemplateColumns: "auto auto 1fr" }}
|
style={{ gridTemplateColumns: "auto auto 1fr" }}
|
||||||
>
|
>
|
||||||
<span className="h-2 w-2 shrink-0 rounded-full bg-green-500 self-center"></span>
|
<span className="h-2 w-2 shrink-0 rounded-full bg-green-500 self-center" />
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center justify-center",
|
"flex items-center justify-center",
|
||||||
@ -151,7 +151,7 @@ export default function ServerCard({
|
|||||||
})}
|
})}
|
||||||
style={{ gridTemplateColumns: "auto auto 1fr" }}
|
style={{ gridTemplateColumns: "auto auto 1fr" }}
|
||||||
>
|
>
|
||||||
<span className="h-2 w-2 shrink-0 rounded-full bg-red-500 self-center"></span>
|
<span className="h-2 w-2 shrink-0 rounded-full bg-red-500 self-center" />
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center justify-center",
|
"flex items-center justify-center",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NezhaAPISafe } from "@/app/types/nezha-api"
|
import type { NezhaAPISafe } from "@/app/types/nezha-api"
|
||||||
import ServerFlag from "@/components/ServerFlag"
|
import ServerFlag from "@/components/ServerFlag"
|
||||||
import ServerUsageBar from "@/components/ServerUsageBar"
|
import ServerUsageBar from "@/components/ServerUsageBar"
|
||||||
import { Card } from "@/components/ui/card"
|
import { Card } from "@/components/ui/card"
|
||||||
@ -36,7 +36,7 @@ export default function ServerCardInline({
|
|||||||
className={cn("grid items-center gap-2 lg:w-36")}
|
className={cn("grid items-center gap-2 lg:w-36")}
|
||||||
style={{ gridTemplateColumns: "auto auto 1fr" }}
|
style={{ gridTemplateColumns: "auto auto 1fr" }}
|
||||||
>
|
>
|
||||||
<span className="h-2 w-2 shrink-0 rounded-full bg-green-500 self-center"></span>
|
<span className="h-2 w-2 shrink-0 rounded-full bg-green-500 self-center" />
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center justify-center",
|
"flex items-center justify-center",
|
||||||
@ -133,7 +133,7 @@ export default function ServerCardInline({
|
|||||||
className={cn("grid items-center gap-2 lg:w-40")}
|
className={cn("grid items-center gap-2 lg:w-40")}
|
||||||
style={{ gridTemplateColumns: "auto auto 1fr" }}
|
style={{ gridTemplateColumns: "auto auto 1fr" }}
|
||||||
>
|
>
|
||||||
<span className="h-2 w-2 shrink-0 rounded-full bg-red-500 self-center"></span>
|
<span className="h-2 w-2 shrink-0 rounded-full bg-red-500 self-center" />
|
||||||
<div
|
<div
|
||||||
className={cn("flex items-center justify-center", showFlag ? "min-w-[17px]" : "min-w-0")}
|
className={cn("flex items-center justify-center", showFlag ? "min-w-[17px]" : "min-w-0")}
|
||||||
>
|
>
|
||||||
|
@ -64,6 +64,7 @@ export function SignIn() {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button
|
||||||
|
type="submit"
|
||||||
className="px-1.5 py-0.5 w-fit flex items-center gap-1 text-sm font-semibold border-stone-300 dark:border-stone-800 rounded-[8px] border bg-card hover:brightness-95 transition-all text-card-foreground shadow-lg shadow-neutral-200/40 dark:shadow-none"
|
className="px-1.5 py-0.5 w-fit flex items-center gap-1 text-sm font-semibold border-stone-300 dark:border-stone-800 rounded-[8px] border bg-card hover:brightness-95 transition-all text-card-foreground shadow-lg shadow-neutral-200/40 dark:shadow-none"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
|
@ -5,7 +5,7 @@ export const Loader = ({ visible }: { visible: boolean }) => {
|
|||||||
<div className="hamster-loading-wrapper" data-visible={visible}>
|
<div className="hamster-loading-wrapper" data-visible={visible}>
|
||||||
<div className="hamster-spinner">
|
<div className="hamster-spinner">
|
||||||
{bars.map((_, i) => (
|
{bars.map((_, i) => (
|
||||||
<div className="hamster-loading-bar" key={`hamster-bar-${i}`} />
|
<div className="hamster-loading-bar" key={`hamster-bar-${i + 1}`} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,16 +7,16 @@ export default function NetworkChartLoading() {
|
|||||||
<CardHeader className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row">
|
<CardHeader className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row">
|
||||||
<div className="flex flex-1 flex-col justify-center gap-1 px-6 py-5">
|
<div className="flex flex-1 flex-col justify-center gap-1 px-6 py-5">
|
||||||
<CardTitle className="flex items-center gap-0.5 text-xl">
|
<CardTitle className="flex items-center gap-0.5 text-xl">
|
||||||
<div className="aspect-auto h-[20px] w-24 bg-muted"></div>
|
<div className="aspect-auto h-[20px] w-24 bg-muted" />
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<div className="mt-[2px] aspect-auto h-[14px] w-32 bg-muted"></div>
|
<div className="mt-[2px] aspect-auto h-[14px] w-32 bg-muted" />
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden pr-4 pt-4 sm:block">
|
<div className="hidden pr-4 pt-4 sm:block">
|
||||||
<Loader visible={true} />
|
<Loader visible={true} />
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="px-2 sm:p-6">
|
<CardContent className="px-2 sm:p-6">
|
||||||
<div className="aspect-auto h-[250px] w-full"></div>
|
<div className="aspect-auto h-[250px] w-full" />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
|
@ -6,12 +6,12 @@ export function ServerDetailChartLoading() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<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">
|
||||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none" />
|
||||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none" />
|
||||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none" />
|
||||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none" />
|
||||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none" />
|
||||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none" />
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -24,14 +24,14 @@ export function ServerDetailLoading() {
|
|||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push(`/`)
|
router.push("/")
|
||||||
}}
|
}}
|
||||||
className="flex flex-none cursor-pointer font-semibold leading-none items-center break-all tracking-tight gap-0.5 text-xl"
|
className="flex flex-none cursor-pointer font-semibold leading-none items-center break-all tracking-tight gap-0.5 text-xl"
|
||||||
>
|
>
|
||||||
<BackIcon />
|
<BackIcon />
|
||||||
<Skeleton className="h-[20px] w-24 rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
<Skeleton className="h-[20px] w-24 rounded-[5px] bg-muted-foreground/10 animate-none" />
|
||||||
</div>
|
</div>
|
||||||
<Skeleton className="flex flex-wrap gap-2 h-[81px] w-1/2 mt-3 rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
<Skeleton className="flex flex-wrap gap-2 h-[81px] w-1/2 mt-3 rounded-[5px] bg-muted-foreground/10 animate-none" />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { type VariantProps, cva } from "class-variance-authority"
|
import { type VariantProps, cva } from "class-variance-authority"
|
||||||
import * as React from "react"
|
import type * as React from "react"
|
||||||
|
|
||||||
const badgeVariants = cva(
|
const badgeVariants = cva(
|
||||||
"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-hidden 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-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use server"
|
"use server"
|
||||||
|
|
||||||
import { NezhaAPI, ServerApi } from "@/app/types/nezha-api"
|
import type { NezhaAPI, ServerApi } from "@/app/types/nezha-api"
|
||||||
import { MakeOptional } from "@/app/types/utils"
|
import type { MakeOptional } from "@/app/types/utils"
|
||||||
import getEnv from "@/lib/env-entry"
|
import getEnv from "@/lib/env-entry"
|
||||||
import { unstable_noStore as noStore } from "next/cache"
|
import { unstable_noStore as noStore } from "next/cache"
|
||||||
|
|
||||||
@ -71,9 +71,9 @@ export async function GetNezhaData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove unwanted properties
|
// Remove unwanted properties
|
||||||
delete element.ipv4
|
element.ipv4 = undefined
|
||||||
delete element.ipv6
|
element.ipv6 = undefined
|
||||||
delete element.valid_ip
|
element.valid_ip = undefined
|
||||||
|
|
||||||
return element
|
return element
|
||||||
},
|
},
|
||||||
@ -213,9 +213,9 @@ export async function GetServerDetail({ server_id }: { server_id: number }) {
|
|||||||
const timestamp = Date.now() / 1000
|
const timestamp = Date.now() / 1000
|
||||||
const detailData = detailDataList.map((element) => {
|
const detailData = detailDataList.map((element) => {
|
||||||
element.online_status = timestamp - element.last_active <= 180
|
element.online_status = timestamp - element.last_active <= 180
|
||||||
delete element.ipv4
|
element.ipv4 = undefined
|
||||||
delete element.ipv6
|
element.ipv6 = undefined
|
||||||
delete element.valid_ip
|
element.valid_ip = undefined
|
||||||
return element
|
return element
|
||||||
})[0]
|
})[0]
|
||||||
|
|
||||||
|
15
lib/utils.ts
15
lib/utils.ts
@ -1,4 +1,4 @@
|
|||||||
import { NezhaAPISafe } from "@/app/types/nezha-api"
|
import type { NezhaAPISafe } from "@/app/types/nezha-api"
|
||||||
import { type ClassValue, clsx } from "clsx"
|
import { type ClassValue, clsx } from "clsx"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ export function formatNezhaInfo(serverInfo: NezhaAPISafe) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatBytes(bytes: number, decimals: number = 2) {
|
export function formatBytes(bytes: number, decimals = 2) {
|
||||||
if (!+bytes) return "0 Bytes"
|
if (!+bytes) return "0 Bytes"
|
||||||
|
|
||||||
const k = 1024
|
const k = 1024
|
||||||
@ -51,7 +51,7 @@ export function formatBytes(bytes: number, decimals: number = 2) {
|
|||||||
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
|
||||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`
|
return `${Number.parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDaysBetweenDates(date1: string, date2: string): number {
|
export function getDaysBetweenDates(date1: string, date2: string): number {
|
||||||
@ -102,11 +102,14 @@ export function formatRelativeTime(timestamp: number): string {
|
|||||||
if (hours > 24) {
|
if (hours > 24) {
|
||||||
const days = Math.floor(hours / 24)
|
const days = Math.floor(hours / 24)
|
||||||
return `${days}d`
|
return `${days}d`
|
||||||
} else if (hours > 0) {
|
}
|
||||||
|
if (hours > 0) {
|
||||||
return `${hours}h`
|
return `${hours}h`
|
||||||
} else if (minutes > 0) {
|
}
|
||||||
|
if (minutes > 0) {
|
||||||
return `${minutes}m`
|
return `${minutes}m`
|
||||||
} else if (seconds >= 0) {
|
}
|
||||||
|
if (seconds >= 0) {
|
||||||
return `${seconds}s`
|
return `${seconds}s`
|
||||||
}
|
}
|
||||||
return "0s"
|
return "0s"
|
||||||
|
Loading…
Reference in New Issue
Block a user