diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..40a4775 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": ["next/core-web-vitals", "next/typescript"], + "rules": { + "@typescript-eslint/no-explicit-any": "off", + "@next/next/no-img-element": "off" + } +} diff --git a/README.md b/README.md index 8793528..71a3189 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ | ----------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------- | | [部署简易教程](https://buycoffee.top/blog/tech/nezha) | [Docker 部署教程](https://buycoffee.top/blog/tech/nezha-docker) | [Cloudflare 部署教程](https://buycoffee.top/blog/tech/nezha-cloudflare) | [更新教程](https://buycoffee.top/blog/tech/nezha-upgrade) | | [Vercel-demo](https://nezha-vercel.buycoffee.top) | [Docker-demo](https://nezha-docker.buycoffee.tech) | [Cloudflare-demo](https://nezha-cloudflare.buycoffee.tech) [密码: nezhadash] | +#### Cloudflare 部署所需环境变量 +NODE_VERSION 22.9.0 +
+BUN_VERSION 1.1.29 #### 环境变量 diff --git a/app/(main)/ClientComponents/NetworkChart.tsx b/app/(main)/ClientComponents/NetworkChart.tsx index 45243e1..6256975 100644 --- a/app/(main)/ClientComponents/NetworkChart.tsx +++ b/app/(main)/ClientComponents/NetworkChart.tsx @@ -2,7 +2,6 @@ import NetworkChartLoading from "@/app/(main)/ClientComponents/NetworkChartLoading"; import { NezhaAPIMonitor, ServerMonitorChart } from "@/app/types/nezha-api"; -import { BackIcon } from "@/components/Icon"; import { Card, CardContent, @@ -21,9 +20,7 @@ import { import getEnv from "@/lib/env-entry"; import { formatTime, nezhaFetcher } from "@/lib/utils"; import { formatRelativeTime } from "@/lib/utils"; -import { useLocale } from "next-intl"; import { useTranslations } from "next-intl"; -import { useRouter } from "next/navigation"; import * as React from "react"; import { useCallback, useMemo } from "react"; import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"; diff --git a/app/(main)/ClientComponents/ServerDetailClient.tsx b/app/(main)/ClientComponents/ServerDetailClient.tsx index ad695bb..98b329a 100644 --- a/app/(main)/ClientComponents/ServerDetailClient.tsx +++ b/app/(main)/ClientComponents/ServerDetailClient.tsx @@ -8,7 +8,7 @@ import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; import getEnv from "@/lib/env-entry"; import { cn, formatBytes, nezhaFetcher } from "@/lib/utils"; -import { useLocale, useTranslations } from "next-intl"; +import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import useSWR from "swr"; @@ -51,7 +51,6 @@ export default function ServerDetailClient({ nezhaFetcher, ); const fallbackData = allFallbackData?.result?.find( - // @ts-ignore (item) => item.id === server_id, ); diff --git a/app/(main)/ClientComponents/ServerDetailLoading.tsx b/app/(main)/ClientComponents/ServerDetailLoading.tsx index 1f26be8..7e09fa1 100644 --- a/app/(main)/ClientComponents/ServerDetailLoading.tsx +++ b/app/(main)/ClientComponents/ServerDetailLoading.tsx @@ -1,6 +1,5 @@ import { BackIcon } from "@/components/Icon"; import { Skeleton } from "@/components/ui/skeleton"; -import { useLocale } from "next-intl"; import { useRouter } from "next/navigation"; export function ServerDetailChartLoading() { diff --git a/app/(main)/[id]/page.tsx b/app/(main)/[id]/page.tsx index ac10cc9..4cfebd2 100644 --- a/app/(main)/[id]/page.tsx +++ b/app/(main)/[id]/page.tsx @@ -5,9 +5,10 @@ import ServerDetailChartClient from "@/app/(main)/ClientComponents/ServerDetailC import ServerDetailClient from "@/app/(main)/ClientComponents/ServerDetailClient"; import TabSwitch from "@/components/TabSwitch"; import { Separator } from "@/components/ui/separator"; -import { useState } from "react"; +import { use, useState } from "react"; -export default function Page({ params }: { params: { id: string } }) { +export default function Page(props: { params: Promise<{ id: string }> }) { + const params = use(props.params); const tabs = ["Detail", "Network"]; const [currentTab, setCurrentTab] = useState(tabs[0]); return ( diff --git a/app/(main)/header.tsx b/app/(main)/header.tsx index 6405647..5da1daf 100644 --- a/app/(main)/header.tsx +++ b/app/(main)/header.tsx @@ -102,8 +102,8 @@ function Links() { // https://github.com/streamich/react-use/blob/master/src/useInterval.ts -const useInterval = (callback: Function, delay?: number | null) => { - const savedCallback = useRef(() => {}); +const useInterval = (callback: () => void, delay: number | null) => { + const savedCallback = useRef<() => void>(() => {}); useEffect(() => { savedCallback.current = callback; }); diff --git a/app/api/detail/route.ts b/app/api/detail/route.ts index a500bce..91ee0e6 100644 --- a/app/api/detail/route.ts +++ b/app/api/detail/route.ts @@ -2,12 +2,19 @@ import { auth } from "@/auth"; import getEnv from "@/lib/env-entry"; import { GetServerDetail } from "@/lib/serverFetch"; import { redirect } from "next/navigation"; -import { NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; export const dynamic = "force-dynamic"; -export const GET = auth(async function GET(req) { - if (!req.auth && getEnv("SitePassword")) { +interface ResError extends Error { + statusCode: number; + message: string; +} + +export async function GET(req: NextRequest) { + const session = await auth(); + + if (!session && getEnv("SitePassword")) { redirect("/"); } @@ -33,11 +40,10 @@ export const GET = auth(async function GET(req) { const detailData = await GetServerDetail({ server_id: serverIdNum }); return NextResponse.json(detailData, { status: 200 }); } catch (error) { - console.error("Error in GET handler:", error); - // @ts-ignore - const statusCode = error.statusCode || 500; - // @ts-ignore - const message = error.message || "Internal Server Error"; + const err = error as ResError; + console.error("Error in GET handler:", err); + const statusCode = err.statusCode || 500; + const message = err.message || "Internal Server Error"; return NextResponse.json({ error: message }, { status: statusCode }); } -}); +} diff --git a/app/api/monitor/route.ts b/app/api/monitor/route.ts index 5a8f204..beb8326 100644 --- a/app/api/monitor/route.ts +++ b/app/api/monitor/route.ts @@ -2,12 +2,19 @@ import { auth } from "@/auth"; import getEnv from "@/lib/env-entry"; import { GetServerMonitor } from "@/lib/serverFetch"; import { redirect } from "next/navigation"; -import { NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; export const dynamic = "force-dynamic"; -export const GET = auth(async function GET(req) { - if (!req.auth && getEnv("SitePassword")) { +interface ResError extends Error { + statusCode: number; + message: string; +} + +export async function GET(req: NextRequest) { + const session = await auth(); + + if (!session && getEnv("SitePassword")) { redirect("/"); } @@ -34,11 +41,10 @@ export const GET = auth(async function GET(req) { }); return NextResponse.json(monitorData, { status: 200 }); } catch (error) { - console.error("Error in GET handler:", error); - // @ts-ignore - const statusCode = error.statusCode || 500; - // @ts-ignore - const message = error.message || "Internal Server Error"; + const err = error as ResError; + console.error("Error in GET handler:", err); + const statusCode = err.statusCode || 500; + const message = err.message || "Internal Server Error"; return NextResponse.json({ error: message }, { status: statusCode }); } -}); +} diff --git a/app/api/server/route.ts b/app/api/server/route.ts index ddfa7da..f54fdec 100644 --- a/app/api/server/route.ts +++ b/app/api/server/route.ts @@ -6,8 +6,15 @@ import { NextResponse } from "next/server"; export const dynamic = "force-dynamic"; -export const GET = auth(async function GET(req) { - if (!req.auth && getEnv("SitePassword")) { +interface ResError extends Error { + statusCode: number; + message: string; +} + +export async function GET() { + const session = await auth(); + + if (!session && getEnv("SitePassword")) { redirect("/"); } @@ -15,11 +22,10 @@ export const GET = auth(async function GET(req) { const data = await GetNezhaData(); return NextResponse.json(data, { status: 200 }); } catch (error) { - console.error("Error in GET handler:", error); - // @ts-ignore - const statusCode = error.statusCode || 500; - // @ts-ignore - const message = error.message || "Internal Server Error"; + const err = error as ResError; + console.error("Error in GET handler:", err); + const statusCode = err.statusCode || 500; + const message = err.message || "Internal Server Error"; return NextResponse.json({ error: message }, { status: statusCode }); } -}); +} diff --git a/bun.lockb b/bun.lockb index a0f7e64..51da8b0 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/ServerCard.tsx b/components/ServerCard.tsx index a54b008..c90d44c 100644 --- a/components/ServerCard.tsx +++ b/components/ServerCard.tsx @@ -10,7 +10,7 @@ import { } from "@/components/ui/popover"; import getEnv from "@/lib/env-entry"; import { cn, formatBytes, formatNezhaInfo } from "@/lib/utils"; -import { useLocale, useTranslations } from "next-intl"; +import { useTranslations } from "next-intl"; import Link from "next/link"; import { useRouter } from "next/navigation"; @@ -21,7 +21,7 @@ export default function ServerCard({ }) { const t = useTranslations("ServerCard"); const router = useRouter(); - const { id, name, country_code, online, cpu, up, down, mem, stg, ...props } = + const { id, name, country_code, online, cpu, up, down, mem, stg } = formatNezhaInfo(serverInfo); const showFlag = getEnv("NEXT_PUBLIC_ShowFlag") === "true"; diff --git a/components/Switch.tsx b/components/Switch.tsx index e84a080..0d6fbe1 100644 --- a/components/Switch.tsx +++ b/components/Switch.tsx @@ -3,7 +3,7 @@ import { cn } from "@/lib/utils"; import { motion } from "framer-motion"; import { useTranslations } from "next-intl"; -import React, { createRef, useEffect, useRef, useState } from "react"; +import React, { createRef, useEffect, useRef } from "react"; export default function Switch({ allTag, @@ -23,7 +23,7 @@ export default function Switch({ if (savedTag && allTag.includes(savedTag)) { onTagChange(savedTag); } - }, [allTag]); + }, [allTag, onTagChange]); useEffect(() => { const container = scrollRef.current; @@ -53,7 +53,7 @@ export default function Switch({ inline: "center", }); } - }, [nowTag]); + }, [nowTag, allTag]); return (
{ const colorConfig = Object.entries(config).filter( - ([_, config]) => config.theme || config.color, + ([, config]) => config.theme || config.color, ); if (!colorConfig.length) { diff --git a/components/ui/input.tsx b/components/ui/input.tsx index 5b0bce3..6e58d5e 100644 --- a/components/ui/input.tsx +++ b/components/ui/input.tsx @@ -1,8 +1,7 @@ import { cn } from "@/lib/utils"; import * as React from "react"; -export interface InputProps - extends React.InputHTMLAttributes {} +export type InputProps = React.InputHTMLAttributes; const Input = React.forwardRef( ({ className, type, ...props }, ref) => { diff --git a/i18n/locale.ts b/i18n/locale.ts index 04fbcce..26fddc0 100644 --- a/i18n/locale.ts +++ b/i18n/locale.ts @@ -6,9 +6,12 @@ import { cookies } from "next/headers"; const COOKIE_NAME = "NEXT_LOCALE"; export async function getUserLocale() { - return cookies().get(COOKIE_NAME)?.value || (getEnv("DefaultLocale") ?? "en"); + return ( + (await cookies()).get(COOKIE_NAME)?.value || + (getEnv("DefaultLocale") ?? "en") + ); } export async function setUserLocale(locale: string) { - cookies().set(COOKIE_NAME, locale); + (await cookies()).set(COOKIE_NAME, locale); } diff --git a/lib/utils.ts b/lib/utils.ts index 024faf3..17d4186 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -76,9 +76,9 @@ export const nezhaFetcher = async (url: string) => { if (!res.ok) { const error = new Error("An error occurred while fetching the data."); - // @ts-ignore + // @ts-expect-error - res.json() returns a Promise error.info = await res.json(); - // @ts-ignore + // @ts-expect-error - res.status is a number error.status = res.status; throw error; } diff --git a/package.json b/package.json index b1d8f9e..9f775aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nezha-dash", - "version": "1.0.0", + "version": "1.1.0", "private": true, "scripts": { "dev": "next dev -p 3020", @@ -22,46 +22,53 @@ "@radix-ui/react-tooltip": "^1.1.3", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/luxon": "^3.4.2", - "@typescript-eslint/eslint-plugin": "^8.10.0", - "caniuse-lite": "^1.0.30001669", + "@typescript-eslint/eslint-plugin": "^8.12.2", + "caniuse-lite": "^1.0.30001674", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "country-flag-icons": "^1.5.13", "eslint-plugin-simple-import-sort": "^12.1.1", "flag-icons": "^7.2.3", - "framer-motion": "^11.11.9", + "framer-motion": "^12.0.0-alpha.1", "lucide-react": "^0.451.0", "luxon": "^3.5.0", - "next": "^14.2.15", + "next": "15.0.2", "next-auth": "^5.0.0-beta.25", - "next-intl": "^3.21.1", + "next-intl": "^3.23.5", "next-runtime-env": "^3.2.2", "next-themes": "^0.3.0", - "react": "^18.3.1", + "react": "19.0.0-rc-02c0e824-20241028", "react-device-detect": "^2.2.3", - "react-dom": "^18.3.1", + "react-dom": "19.0.0-rc-02c0e824-20241028", "react-intersection-observer": "^9.13.1", "react-wrap-balancer": "^1.1.1", - "recharts": "2.12.7", + "recharts": "2.13.1", "sharp": "^0.33.5", "swr": "^2.2.6-beta.4", "tailwind-merge": "^2.5.4", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "typescript-eslint": "^8.12.2" }, "devDependencies": { - "eslint-plugin-turbo": "^2.2.1", + "eslint-plugin-turbo": "^2.2.3", "eslint-plugin-unused-imports": "^4.1.4", - "@next/bundle-analyzer": "^14.2.15", - "@types/node": "^22.7.7", - "@types/react": "^18.3.11", - "@types/react-dom": "^18.3.1", + "@next/bundle-analyzer": "15.0.2", + "@types/node": "^22.8.4", + "@types/react": "npm:types-react@19.0.0-rc.1", + "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1", "autoprefixer": "^10.4.20", "eslint": "^9.13.0", - "eslint-config-next": "^14.2.15", + "eslint-config-next": "15.0.2", "postcss": "^8.4.47", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8", "tailwindcss": "^3.4.14", "typescript": "^5.6.3" - } + }, + "overrides": { + "@types/react": "npm:types-react@19.0.0-rc.1", + "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1", + "react-is": "^19.0.0-rc-69d4b800-20241021" + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/public/manifest.json b/public/manifest.json index e00a07d..7d0dadc 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { - "name": "HomeDash", - "short_name": "HomeDash PWA App", + "name": "NezhaDash PWA App", + "short_name": "NezhaDash", "icons": [ { "src": "/android-chrome-192x192.png",