mirror of
https://github.com/hamster1963/nezha-dash.git
synced 2025-04-24 21:10:45 +08:00
Compare commits
5 Commits
68b7034db6
...
cc97147270
Author | SHA1 | Date | |
---|---|---|---|
|
cc97147270 | ||
|
6bc7e0de0e | ||
|
6ba7747dd6 | ||
|
15086d054a | ||
|
6979139c98 |
@ -1,6 +1,10 @@
|
||||
"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 { ServerDetailChartLoading } from "@/components/loading/ServerDetailLoading"
|
||||
import AnimatedCircularProgressBar from "@/components/ui/animated-circular-progress-bar"
|
||||
@ -122,7 +126,7 @@ function CpuChart({ history, data }: { history: ServerDataWithTimestamp[]; data:
|
||||
} else {
|
||||
newData = [...cpuChartData, { timeStamp: timestamp, cpu: cpu }]
|
||||
}
|
||||
if (newData.length > 30) {
|
||||
if (newData.length > MAX_HISTORY_LENGTH) {
|
||||
newData.shift()
|
||||
}
|
||||
setCpuChartData(newData)
|
||||
@ -245,7 +249,7 @@ function ProcessChart({
|
||||
} else {
|
||||
newData = [...processChartData, { timeStamp: timestamp, process: process }]
|
||||
}
|
||||
if (newData.length > 30) {
|
||||
if (newData.length > MAX_HISTORY_LENGTH) {
|
||||
newData.shift()
|
||||
}
|
||||
setProcessChartData(newData)
|
||||
@ -349,7 +353,7 @@ function MemChart({ data, history }: { data: NezhaAPISafe; history: ServerDataWi
|
||||
} else {
|
||||
newData = [...memChartData, { timeStamp: timestamp, mem: mem, swap: swap }]
|
||||
}
|
||||
if (newData.length > 30) {
|
||||
if (newData.length > MAX_HISTORY_LENGTH) {
|
||||
newData.shift()
|
||||
}
|
||||
setMemChartData(newData)
|
||||
@ -502,7 +506,7 @@ function DiskChart({ data, history }: { data: NezhaAPISafe; history: ServerDataW
|
||||
} else {
|
||||
newData = [...diskChartData, { timeStamp: timestamp, disk: disk }]
|
||||
}
|
||||
if (newData.length > 30) {
|
||||
if (newData.length > MAX_HISTORY_LENGTH) {
|
||||
newData.shift()
|
||||
}
|
||||
setDiskChartData(newData)
|
||||
@ -631,7 +635,7 @@ function NetworkChart({
|
||||
} else {
|
||||
newData = [...networkChartData, { timeStamp: timestamp, upload: up, download: down }]
|
||||
}
|
||||
if (newData.length > 30) {
|
||||
if (newData.length > MAX_HISTORY_LENGTH) {
|
||||
newData.shift()
|
||||
}
|
||||
setNetworkChartData(newData)
|
||||
@ -779,7 +783,7 @@ function ConnectChart({
|
||||
} else {
|
||||
newData = [...connectChartData, { timeStamp: timestamp, tcp: tcp, udp: udp }]
|
||||
}
|
||||
if (newData.length > 30) {
|
||||
if (newData.length > MAX_HISTORY_LENGTH) {
|
||||
newData.shift()
|
||||
}
|
||||
setConnectChartData(newData)
|
||||
|
@ -1,12 +1,11 @@
|
||||
"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 GlobalLoading from "../../../../components/loading/GlobalLoading"
|
||||
import { geoJsonString } from "../../../../lib/geo-json-string"
|
||||
import { TooltipProvider } from "../detail/TooltipContext"
|
||||
import GlobalInfo from "./GlobalInfo"
|
||||
import { InteractiveMap } from "./InteractiveMap"
|
||||
import GlobalLoading from "@/components/loading/GlobalLoading"
|
||||
import { geoJsonString } from "@/lib/geo-json-string"
|
||||
|
||||
export default function ServerGlobal() {
|
||||
const { data: nezhaServerList, error } = useServerData()
|
||||
|
@ -1,11 +1,10 @@
|
||||
"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 { geoEquirectangular, geoPath } from "d3-geo"
|
||||
|
||||
import { useTooltip } from "../detail/TooltipContext"
|
||||
import MapTooltip from "./MapTooltip"
|
||||
|
||||
interface InteractiveMapProps {
|
||||
countries: string[]
|
||||
serverCounts: { [key: string]: number }
|
||||
|
@ -1,10 +1,9 @@
|
||||
"use client"
|
||||
|
||||
import { useTooltip } from "@/app/(main)/ClientComponents/detail/TooltipContext"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { memo } from "react"
|
||||
|
||||
import { useTooltip } from "../detail/TooltipContext"
|
||||
|
||||
const MapTooltip = memo(function MapTooltip() {
|
||||
const { tooltipData } = useTooltip()
|
||||
const t = useTranslations("Global")
|
||||
|
@ -4,6 +4,7 @@ import { useServerData } from "@/app/lib/server-data-context"
|
||||
import ServerCard from "@/components/ServerCard"
|
||||
import ServerCardInline from "@/components/ServerCardInline"
|
||||
import Switch from "@/components/Switch"
|
||||
import GlobalLoading from "@/components/loading/GlobalLoading"
|
||||
import { Loader } from "@/components/loading/Loader"
|
||||
import getEnv from "@/lib/env-entry"
|
||||
import { useFilter } from "@/lib/network-filter-context"
|
||||
@ -14,8 +15,6 @@ import { useTranslations } from "next-intl"
|
||||
import dynamic from "next/dynamic"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
||||
import GlobalLoading from "../../../../components/loading/GlobalLoading"
|
||||
|
||||
const ServerGlobal = dynamic(() => import("./Global"), {
|
||||
ssr: false,
|
||||
loading: () => <GlobalLoading />,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import ServerListClient from "./ClientComponents/main/ServerListClient"
|
||||
import ServerOverviewClient from "./ClientComponents/main/ServerOverviewClient"
|
||||
import ServerListClient from "@/app/(main)/ClientComponents/main/ServerListClient"
|
||||
import ServerOverviewClient from "@/app/(main)/ClientComponents/main/ServerOverviewClient"
|
||||
|
||||
export default async function Home() {
|
||||
return (
|
||||
|
@ -3,13 +3,12 @@
|
||||
import { NetworkChartClient } from "@/app/(main)/ClientComponents/detail/NetworkChart"
|
||||
import ServerDetailChartClient from "@/app/(main)/ClientComponents/detail/ServerDetailChartClient"
|
||||
import ServerDetailClient from "@/app/(main)/ClientComponents/detail/ServerDetailClient"
|
||||
import ServerIPInfo from "@/app/(main)/ClientComponents/detail/ServerIPInfo"
|
||||
import TabSwitch from "@/components/TabSwitch"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import getEnv from "@/lib/env-entry"
|
||||
import { use, useState } from "react"
|
||||
|
||||
import ServerIPInfo from "../../ClientComponents/detail/ServerIPInfo"
|
||||
|
||||
export default function Page(props: { params: Promise<{ id: string }> }) {
|
||||
const params = use(props.params)
|
||||
const tabs = ["Detail", "Network"]
|
||||
|
@ -20,7 +20,7 @@ interface ServerDataContextType {
|
||||
|
||||
const ServerDataContext = createContext<ServerDataContextType | undefined>(undefined)
|
||||
|
||||
const MAX_HISTORY_LENGTH = 30
|
||||
export const MAX_HISTORY_LENGTH = 30
|
||||
|
||||
export function ServerDataProvider({ children }: { children: ReactNode }) {
|
||||
const [history, setHistory] = useState<ServerDataWithTimestamp[]>([])
|
||||
|
@ -1,9 +1,8 @@
|
||||
import Footer from "@/app/(main)/footer"
|
||||
import Header from "@/app/(main)/header"
|
||||
import { useTranslations } from "next-intl"
|
||||
import Link from "next/link"
|
||||
|
||||
import Footer from "./(main)/footer"
|
||||
import Header from "./(main)/header"
|
||||
|
||||
export default function NotFoundPage() {
|
||||
const t = useTranslations("NotFoundPage")
|
||||
return (
|
||||
|
3
auth.ts
3
auth.ts
@ -1,8 +1,7 @@
|
||||
import getEnv from "@/lib/env-entry"
|
||||
import NextAuth from "next-auth"
|
||||
import CredentialsProvider from "next-auth/providers/credentials"
|
||||
|
||||
import getEnv from "./lib/env-entry"
|
||||
|
||||
export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
secret: process.env.AUTH_SECRET ?? "this_is_nezha_dash_web_secret",
|
||||
trustHost: (process.env.AUTH_TRUST_HOST as boolean | undefined) ?? true,
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
@ -41,7 +41,7 @@ export function LanguageSwitcher() {
|
||||
onSelect={(e) => handleSelect(e, item.code)}
|
||||
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,
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
@ -37,19 +37,19 @@ export function ModeToggle() {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="flex flex-col gap-0.5" align="end">
|
||||
<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")}
|
||||
>
|
||||
{t("Light")} {theme === "light" && <CheckCircleIcon className="size-4" />}
|
||||
</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")}
|
||||
>
|
||||
{t("Dark")} {theme === "dark" && <CheckCircleIcon className="size-4" />}
|
||||
</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")}
|
||||
>
|
||||
{t("System")} {theme === "system" && <CheckCircleIcon className="size-4" />}
|
||||
|
@ -80,7 +80,7 @@ const DropdownMenuItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
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",
|
||||
className,
|
||||
)}
|
||||
|
@ -27,7 +27,7 @@
|
||||
"@radix-ui/react-slot": "^1.1.1",
|
||||
"@radix-ui/react-switch": "^1.1.2",
|
||||
"@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",
|
||||
"@types/d3-geo": "^3.1.0",
|
||||
"@types/luxon": "^3.4.2",
|
||||
@ -64,7 +64,7 @@
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@next/bundle-analyzer": "^15.1.3",
|
||||
"@tailwindcss/postcss": "^4.0.0-beta.8",
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/react": "^19.0.2",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"eslint-config-next": "^15.1.3",
|
||||
@ -73,7 +73,7 @@
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^4.0.0-beta.8",
|
||||
"typescript": "^5.7.2",
|
||||
"vercel": "^39.2.2"
|
||||
"vercel": "^39.2.4"
|
||||
},
|
||||
"overrides": {
|
||||
"react-is": "^19.0.0-rc-69d4b800-20241021"
|
||||
|
Loading…
Reference in New Issue
Block a user