Compare commits

...

5 Commits

Author SHA1 Message Date
hamster1963
cc97147270 style: dropdown text display 2025-01-04 00:28:50 +08:00
hamster1963
6bc7e0de0e fix: use MAX_HISTORY_LENGTH config 2025-01-04 00:21:42 +08:00
hamster1963
6ba7747dd6 fix: lint 2025-01-04 00:12:52 +08:00
hamster1963
15086d054a chore: deps 2025-01-04 00:00:39 +08:00
hamster1963
6979139c98 fix: remove used code 2025-01-04 00:00:11 +08:00
17 changed files with 35 additions and 138 deletions

View File

@ -1,6 +1,10 @@
"use client" "use client"
import { ServerDataWithTimestamp, useServerData } from "@/app/lib/server-data-context" import {
MAX_HISTORY_LENGTH,
ServerDataWithTimestamp,
useServerData,
} from "@/app/lib/server-data-context"
import { NezhaAPISafe } from "@/app/types/nezha-api" import { 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"
@ -122,7 +126,7 @@ function CpuChart({ history, data }: { history: ServerDataWithTimestamp[]; data:
} else { } else {
newData = [...cpuChartData, { timeStamp: timestamp, cpu: cpu }] newData = [...cpuChartData, { timeStamp: timestamp, cpu: cpu }]
} }
if (newData.length > 30) { if (newData.length > MAX_HISTORY_LENGTH) {
newData.shift() newData.shift()
} }
setCpuChartData(newData) setCpuChartData(newData)
@ -245,7 +249,7 @@ function ProcessChart({
} else { } else {
newData = [...processChartData, { timeStamp: timestamp, process: process }] newData = [...processChartData, { timeStamp: timestamp, process: process }]
} }
if (newData.length > 30) { if (newData.length > MAX_HISTORY_LENGTH) {
newData.shift() newData.shift()
} }
setProcessChartData(newData) setProcessChartData(newData)
@ -349,7 +353,7 @@ function MemChart({ data, history }: { data: NezhaAPISafe; history: ServerDataWi
} else { } else {
newData = [...memChartData, { timeStamp: timestamp, mem: mem, swap: swap }] newData = [...memChartData, { timeStamp: timestamp, mem: mem, swap: swap }]
} }
if (newData.length > 30) { if (newData.length > MAX_HISTORY_LENGTH) {
newData.shift() newData.shift()
} }
setMemChartData(newData) setMemChartData(newData)
@ -502,7 +506,7 @@ function DiskChart({ data, history }: { data: NezhaAPISafe; history: ServerDataW
} else { } else {
newData = [...diskChartData, { timeStamp: timestamp, disk: disk }] newData = [...diskChartData, { timeStamp: timestamp, disk: disk }]
} }
if (newData.length > 30) { if (newData.length > MAX_HISTORY_LENGTH) {
newData.shift() newData.shift()
} }
setDiskChartData(newData) setDiskChartData(newData)
@ -631,7 +635,7 @@ function NetworkChart({
} else { } else {
newData = [...networkChartData, { timeStamp: timestamp, upload: up, download: down }] newData = [...networkChartData, { timeStamp: timestamp, upload: up, download: down }]
} }
if (newData.length > 30) { if (newData.length > MAX_HISTORY_LENGTH) {
newData.shift() newData.shift()
} }
setNetworkChartData(newData) setNetworkChartData(newData)
@ -779,7 +783,7 @@ function ConnectChart({
} else { } else {
newData = [...connectChartData, { timeStamp: timestamp, tcp: tcp, udp: udp }] newData = [...connectChartData, { timeStamp: timestamp, tcp: tcp, udp: udp }]
} }
if (newData.length > 30) { if (newData.length > MAX_HISTORY_LENGTH) {
newData.shift() newData.shift()
} }
setConnectChartData(newData) setConnectChartData(newData)

View File

@ -1,12 +1,11 @@
"use client" "use client"
import { TooltipProvider } from "@/app/(main)/ClientComponents/detail/TooltipContext"
import GlobalInfo from "@/app/(main)/ClientComponents/main/GlobalInfo"
import { InteractiveMap } from "@/app/(main)/ClientComponents/main/InteractiveMap"
import { useServerData } from "@/app/lib/server-data-context" import { useServerData } from "@/app/lib/server-data-context"
import GlobalLoading from "@/components/loading/GlobalLoading"
import GlobalLoading from "../../../../components/loading/GlobalLoading" import { geoJsonString } from "@/lib/geo-json-string"
import { geoJsonString } from "../../../../lib/geo-json-string"
import { TooltipProvider } from "../detail/TooltipContext"
import GlobalInfo from "./GlobalInfo"
import { InteractiveMap } from "./InteractiveMap"
export default function ServerGlobal() { export default function ServerGlobal() {
const { data: nezhaServerList, error } = useServerData() const { data: nezhaServerList, error } = useServerData()

View File

@ -1,11 +1,10 @@
"use client" "use client"
import { useTooltip } from "@/app/(main)/ClientComponents/detail/TooltipContext"
import MapTooltip from "@/app/(main)/ClientComponents/main/MapTooltip"
import { countryCoordinates } from "@/lib/geo-limit" import { countryCoordinates } from "@/lib/geo-limit"
import { geoEquirectangular, geoPath } from "d3-geo" import { geoEquirectangular, geoPath } from "d3-geo"
import { useTooltip } from "../detail/TooltipContext"
import MapTooltip from "./MapTooltip"
interface InteractiveMapProps { interface InteractiveMapProps {
countries: string[] countries: string[]
serverCounts: { [key: string]: number } serverCounts: { [key: string]: number }

View File

@ -1,10 +1,9 @@
"use client" "use client"
import { useTooltip } from "@/app/(main)/ClientComponents/detail/TooltipContext"
import { useTranslations } from "next-intl" import { useTranslations } from "next-intl"
import { memo } from "react" import { memo } from "react"
import { useTooltip } from "../detail/TooltipContext"
const MapTooltip = memo(function MapTooltip() { const MapTooltip = memo(function MapTooltip() {
const { tooltipData } = useTooltip() const { tooltipData } = useTooltip()
const t = useTranslations("Global") const t = useTranslations("Global")

View File

@ -4,6 +4,7 @@ import { useServerData } from "@/app/lib/server-data-context"
import ServerCard from "@/components/ServerCard" import ServerCard from "@/components/ServerCard"
import ServerCardInline from "@/components/ServerCardInline" import ServerCardInline from "@/components/ServerCardInline"
import Switch from "@/components/Switch" import Switch from "@/components/Switch"
import GlobalLoading from "@/components/loading/GlobalLoading"
import { Loader } from "@/components/loading/Loader" import { Loader } from "@/components/loading/Loader"
import getEnv from "@/lib/env-entry" import getEnv from "@/lib/env-entry"
import { useFilter } from "@/lib/network-filter-context" import { useFilter } from "@/lib/network-filter-context"
@ -14,8 +15,6 @@ import { useTranslations } from "next-intl"
import dynamic from "next/dynamic" import dynamic from "next/dynamic"
import { useEffect, useRef, useState } from "react" import { useEffect, useRef, useState } from "react"
import GlobalLoading from "../../../../components/loading/GlobalLoading"
const ServerGlobal = dynamic(() => import("./Global"), { const ServerGlobal = dynamic(() => import("./Global"), {
ssr: false, ssr: false,
loading: () => <GlobalLoading />, loading: () => <GlobalLoading />,

View File

@ -1,5 +1,5 @@
import ServerListClient from "./ClientComponents/main/ServerListClient" import ServerListClient from "@/app/(main)/ClientComponents/main/ServerListClient"
import ServerOverviewClient from "./ClientComponents/main/ServerOverviewClient" import ServerOverviewClient from "@/app/(main)/ClientComponents/main/ServerOverviewClient"
export default async function Home() { export default async function Home() {
return ( return (

View File

@ -3,13 +3,12 @@
import { NetworkChartClient } from "@/app/(main)/ClientComponents/detail/NetworkChart" import { NetworkChartClient } from "@/app/(main)/ClientComponents/detail/NetworkChart"
import ServerDetailChartClient from "@/app/(main)/ClientComponents/detail/ServerDetailChartClient" import ServerDetailChartClient from "@/app/(main)/ClientComponents/detail/ServerDetailChartClient"
import ServerDetailClient from "@/app/(main)/ClientComponents/detail/ServerDetailClient" import ServerDetailClient from "@/app/(main)/ClientComponents/detail/ServerDetailClient"
import ServerIPInfo from "@/app/(main)/ClientComponents/detail/ServerIPInfo"
import TabSwitch from "@/components/TabSwitch" import TabSwitch from "@/components/TabSwitch"
import { Separator } from "@/components/ui/separator" import { Separator } from "@/components/ui/separator"
import getEnv from "@/lib/env-entry" import getEnv from "@/lib/env-entry"
import { use, useState } from "react" import { use, useState } from "react"
import ServerIPInfo from "../../ClientComponents/detail/ServerIPInfo"
export default function Page(props: { params: Promise<{ id: string }> }) { export default function Page(props: { params: Promise<{ id: string }> }) {
const params = use(props.params) const params = use(props.params)
const tabs = ["Detail", "Network"] const tabs = ["Detail", "Network"]

View File

@ -20,7 +20,7 @@ interface ServerDataContextType {
const ServerDataContext = createContext<ServerDataContextType | undefined>(undefined) const ServerDataContext = createContext<ServerDataContextType | undefined>(undefined)
const MAX_HISTORY_LENGTH = 30 export const MAX_HISTORY_LENGTH = 30
export function ServerDataProvider({ children }: { children: ReactNode }) { export function ServerDataProvider({ children }: { children: ReactNode }) {
const [history, setHistory] = useState<ServerDataWithTimestamp[]>([]) const [history, setHistory] = useState<ServerDataWithTimestamp[]>([])

View File

@ -1,9 +1,8 @@
import Footer from "@/app/(main)/footer"
import Header from "@/app/(main)/header"
import { useTranslations } from "next-intl" import { useTranslations } from "next-intl"
import Link from "next/link" import Link from "next/link"
import Footer from "./(main)/footer"
import Header from "./(main)/header"
export default function NotFoundPage() { export default function NotFoundPage() {
const t = useTranslations("NotFoundPage") const t = useTranslations("NotFoundPage")
return ( return (

View File

@ -1,8 +1,7 @@
import getEnv from "@/lib/env-entry"
import NextAuth from "next-auth" import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials" import CredentialsProvider from "next-auth/providers/credentials"
import getEnv from "./lib/env-entry"
export const { handlers, signIn, signOut, auth } = NextAuth({ export const { handlers, signIn, signOut, auth } = NextAuth({
secret: process.env.AUTH_SECRET ?? "this_is_nezha_dash_web_secret", secret: process.env.AUTH_SECRET ?? "this_is_nezha_dash_web_secret",
trustHost: (process.env.AUTH_TRUST_HOST as boolean | undefined) ?? true, trustHost: (process.env.AUTH_TRUST_HOST as boolean | undefined) ?? true,

BIN
bun.lockb

Binary file not shown.

View File

@ -1,31 +0,0 @@
"use client"
import { useFilter } from "@/lib/network-filter-context"
import { useStatus } from "@/lib/status-context"
import { ServerStackIcon } from "@heroicons/react/20/solid"
import { useRouter } from "next/navigation"
import { useEffect } from "react"
export default function GlobalBackButton() {
const router = useRouter()
const { setStatus } = useStatus()
const { setFilter } = useFilter()
useEffect(() => {
setStatus("all")
setFilter(false)
sessionStorage.removeItem("selectedTag")
router.prefetch(`/`)
}, [])
return (
<button
onClick={() => {
router.push(`/`)
}}
className="rounded-[50px] mt-[1px] w-fit text-white cursor-pointer [text-shadow:_0_1px_0_rgb(0_0_0_/_20%)] bg-green-600 hover:bg-green-500 p-[10px] transition-all shadow-[inset_0_1px_0_rgba(255,255,255,0.2)] hover:shadow-[inset_0_1px_0_rgba(0,0,0,0.2)] "
>
<ServerStackIcon className="size-[13px]" />
</button>
)
}

View File

@ -41,7 +41,7 @@ export function LanguageSwitcher() {
onSelect={(e) => handleSelect(e, item.code)} onSelect={(e) => handleSelect(e, item.code)}
className={cn( className={cn(
{ {
"bg-muted gap-3": locale === item.code, "bg-muted gap-3 font-semibold": locale === item.code,
}, },
{ {
"rounded-t-[5px]": index === localeItems.length - 1, "rounded-t-[5px]": index === localeItems.length - 1,

View File

@ -1,69 +0,0 @@
import { NezhaAPISafe } from "@/app/types/nezha-api"
import { cn, formatBytes } from "@/lib/utils"
import { useTranslations } from "next-intl"
export function ServerCardPopoverCard({
className,
title,
content,
children,
}: {
className?: string
title: string
content?: string
children?: React.ReactNode
}) {
return (
<div className={cn("mb-[6px] flex w-full flex-col", className)}>
<div className="text-sm font-semibold">{title}</div>
{children ? children : <div className="break-all text-xs font-medium">{content}</div>}
</div>
)
}
export default function ServerCardPopover({
host,
status,
}: {
host: NezhaAPISafe["host"]
status: NezhaAPISafe["status"]
}) {
const t = useTranslations("ServerCardPopover")
return (
<section className="max-w-[300px]">
<ServerCardPopoverCard
title={t("System")}
content={`${host.Platform}-${host.PlatformVersion} [${host.Virtualization}: ${host.Arch}]`}
/>
<ServerCardPopoverCard
title={t("CPU")}
content={`${host.CPU.map((item) => item).join(", ")}`}
/>
<ServerCardPopoverCard
title={t("Mem")}
content={`${formatBytes(status.MemUsed)} / ${formatBytes(host.MemTotal)}`}
/>
<ServerCardPopoverCard
title={t("STG")}
content={`${formatBytes(status.DiskUsed)} / ${formatBytes(host.DiskTotal)}`}
/>
<ServerCardPopoverCard
title={t("Swap")}
content={`${formatBytes(status.SwapUsed)} / ${formatBytes(host.SwapTotal)}`}
/>
<ServerCardPopoverCard
title={t("Network")}
content={`${formatBytes(status.NetOutTransfer)} / ${formatBytes(status.NetInTransfer)}`}
/>
<ServerCardPopoverCard
title={t("Load")}
content={`${status.Load1.toFixed(2)} / ${status.Load5.toFixed(2)} / ${status.Load15.toFixed(2)}`}
/>
<ServerCardPopoverCard
className="mb-0"
title={t("Online")}
content={`${(status.Uptime / 86400).toFixed(0)} Days`}
/>
</section>
)
}

View File

@ -37,19 +37,19 @@ export function ModeToggle() {
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent className="flex flex-col gap-0.5" align="end"> <DropdownMenuContent className="flex flex-col gap-0.5" align="end">
<DropdownMenuItem <DropdownMenuItem
className={cn("rounded-b-[5px]", { "gap-3 bg-muted": theme === "light" })} className={cn("rounded-b-[5px]", { "gap-3 bg-muted font-semibold": theme === "light" })}
onSelect={(e) => handleSelect(e, "light")} onSelect={(e) => handleSelect(e, "light")}
> >
{t("Light")} {theme === "light" && <CheckCircleIcon className="size-4" />} {t("Light")} {theme === "light" && <CheckCircleIcon className="size-4" />}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
className={cn("rounded-[5px]", { "gap-3 bg-muted": theme === "dark" })} className={cn("rounded-[5px]", { "gap-3 bg-muted font-semibold": theme === "dark" })}
onSelect={(e) => handleSelect(e, "dark")} onSelect={(e) => handleSelect(e, "dark")}
> >
{t("Dark")} {theme === "dark" && <CheckCircleIcon className="size-4" />} {t("Dark")} {theme === "dark" && <CheckCircleIcon className="size-4" />}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
className={cn("rounded-t-[5px]", { "gap-3 bg-muted": theme === "system" })} className={cn("rounded-t-[5px]", { "gap-3 bg-muted font-semibold": theme === "system" })}
onSelect={(e) => handleSelect(e, "system")} onSelect={(e) => handleSelect(e, "system")}
> >
{t("System")} {theme === "system" && <CheckCircleIcon className="size-4" />} {t("System")} {theme === "system" && <CheckCircleIcon className="size-4" />}

View File

@ -80,7 +80,7 @@ const DropdownMenuItem = React.forwardRef<
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-[10px] px-2 py-1.5 text-xs font-medium outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", "relative flex cursor-default select-none items-center rounded-[10px] px-2 py-1.5 text-xs font-normal outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
inset && "pl-8", inset && "pl-8",
className, className,
)} )}

View File

@ -27,7 +27,7 @@
"@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.6", "@radix-ui/react-tooltip": "^1.1.6",
"@trivago/prettier-plugin-sort-imports": "^5.2.0", "@trivago/prettier-plugin-sort-imports": "^5.2.1",
"@turf/turf": "^7.2.0", "@turf/turf": "^7.2.0",
"@types/d3-geo": "^3.1.0", "@types/d3-geo": "^3.1.0",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
@ -64,7 +64,7 @@
"@biomejs/biome": "1.9.4", "@biomejs/biome": "1.9.4",
"@next/bundle-analyzer": "^15.1.3", "@next/bundle-analyzer": "^15.1.3",
"@tailwindcss/postcss": "^4.0.0-beta.8", "@tailwindcss/postcss": "^4.0.0-beta.8",
"@types/node": "^22.10.2", "@types/node": "^22.10.5",
"@types/react": "^19.0.2", "@types/react": "^19.0.2",
"@types/react-dom": "^19.0.2", "@types/react-dom": "^19.0.2",
"eslint-config-next": "^15.1.3", "eslint-config-next": "^15.1.3",
@ -73,7 +73,7 @@
"postcss": "^8.4.49", "postcss": "^8.4.49",
"tailwindcss": "^4.0.0-beta.8", "tailwindcss": "^4.0.0-beta.8",
"typescript": "^5.7.2", "typescript": "^5.7.2",
"vercel": "^39.2.2" "vercel": "^39.2.4"
}, },
"overrides": { "overrides": {
"react-is": "^19.0.0-rc-69d4b800-20241021" "react-is": "^19.0.0-rc-69d4b800-20241021"