Merge branch 'hamster1963:main' into main

This commit is contained in:
kattocloud 2024-10-30 14:30:22 +08:00 committed by GitHub
commit 51f7173f91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 102 additions and 68 deletions

7
.eslintrc.json Normal file
View File

@ -0,0 +1,7 @@
{
"extends": ["next/core-web-vitals", "next/typescript"],
"rules": {
"@typescript-eslint/no-explicit-any": "off",
"@next/next/no-img-element": "off"
}
}

View File

@ -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) | | [部署简易教程](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] | | [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
<br>
BUN_VERSION 1.1.29
#### 环境变量 #### 环境变量

View File

@ -2,7 +2,6 @@
import NetworkChartLoading from "@/app/(main)/ClientComponents/NetworkChartLoading"; import NetworkChartLoading from "@/app/(main)/ClientComponents/NetworkChartLoading";
import { NezhaAPIMonitor, ServerMonitorChart } from "@/app/types/nezha-api"; import { NezhaAPIMonitor, ServerMonitorChart } from "@/app/types/nezha-api";
import { BackIcon } from "@/components/Icon";
import { import {
Card, Card,
CardContent, CardContent,
@ -21,9 +20,7 @@ import {
import getEnv from "@/lib/env-entry"; import getEnv from "@/lib/env-entry";
import { formatTime, nezhaFetcher } from "@/lib/utils"; import { formatTime, nezhaFetcher } from "@/lib/utils";
import { formatRelativeTime } from "@/lib/utils"; import { formatRelativeTime } from "@/lib/utils";
import { useLocale } from "next-intl";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import * as React from "react"; import * as React from "react";
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"; import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";

View File

@ -8,7 +8,7 @@ import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import getEnv from "@/lib/env-entry"; import getEnv from "@/lib/env-entry";
import { cn, formatBytes, nezhaFetcher } from "@/lib/utils"; import { cn, formatBytes, nezhaFetcher } from "@/lib/utils";
import { useLocale, useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
@ -51,7 +51,6 @@ export default function ServerDetailClient({
nezhaFetcher, nezhaFetcher,
); );
const fallbackData = allFallbackData?.result?.find( const fallbackData = allFallbackData?.result?.find(
// @ts-ignore
(item) => item.id === server_id, (item) => item.id === server_id,
); );

View File

@ -1,6 +1,5 @@
import { BackIcon } from "@/components/Icon"; import { BackIcon } from "@/components/Icon";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { useLocale } from "next-intl";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
export function ServerDetailChartLoading() { export function ServerDetailChartLoading() {

View File

@ -5,9 +5,10 @@ import ServerDetailChartClient from "@/app/(main)/ClientComponents/ServerDetailC
import ServerDetailClient from "@/app/(main)/ClientComponents/ServerDetailClient"; import ServerDetailClient from "@/app/(main)/ClientComponents/ServerDetailClient";
import TabSwitch from "@/components/TabSwitch"; import TabSwitch from "@/components/TabSwitch";
import { Separator } from "@/components/ui/separator"; 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 tabs = ["Detail", "Network"];
const [currentTab, setCurrentTab] = useState(tabs[0]); const [currentTab, setCurrentTab] = useState(tabs[0]);
return ( return (

View File

@ -102,8 +102,8 @@ function Links() {
// https://github.com/streamich/react-use/blob/master/src/useInterval.ts // https://github.com/streamich/react-use/blob/master/src/useInterval.ts
const useInterval = (callback: Function, delay?: number | null) => { const useInterval = (callback: () => void, delay: number | null) => {
const savedCallback = useRef<Function>(() => {}); const savedCallback = useRef<() => void>(() => {});
useEffect(() => { useEffect(() => {
savedCallback.current = callback; savedCallback.current = callback;
}); });

View File

@ -2,12 +2,19 @@ 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 { NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
export const dynamic = "force-dynamic"; export const dynamic = "force-dynamic";
export const GET = auth(async function GET(req) { interface ResError extends Error {
if (!req.auth && getEnv("SitePassword")) { statusCode: number;
message: string;
}
export async function GET(req: NextRequest) {
const session = await auth();
if (!session && getEnv("SitePassword")) {
redirect("/"); redirect("/");
} }
@ -33,11 +40,10 @@ export const GET = auth(async function GET(req) {
const detailData = await GetServerDetail({ server_id: serverIdNum }); const detailData = await GetServerDetail({ server_id: serverIdNum });
return NextResponse.json(detailData, { status: 200 }); return NextResponse.json(detailData, { status: 200 });
} catch (error) { } catch (error) {
console.error("Error in GET handler:", error); const err = error as ResError;
// @ts-ignore console.error("Error in GET handler:", err);
const statusCode = error.statusCode || 500; const statusCode = err.statusCode || 500;
// @ts-ignore const message = err.message || "Internal Server Error";
const message = error.message || "Internal Server Error";
return NextResponse.json({ error: message }, { status: statusCode }); return NextResponse.json({ error: message }, { status: statusCode });
} }
}); }

View File

@ -2,12 +2,19 @@ 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 { NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
export const dynamic = "force-dynamic"; export const dynamic = "force-dynamic";
export const GET = auth(async function GET(req) { interface ResError extends Error {
if (!req.auth && getEnv("SitePassword")) { statusCode: number;
message: string;
}
export async function GET(req: NextRequest) {
const session = await auth();
if (!session && getEnv("SitePassword")) {
redirect("/"); redirect("/");
} }
@ -34,11 +41,10 @@ export const GET = auth(async function GET(req) {
}); });
return NextResponse.json(monitorData, { status: 200 }); return NextResponse.json(monitorData, { status: 200 });
} catch (error) { } catch (error) {
console.error("Error in GET handler:", error); const err = error as ResError;
// @ts-ignore console.error("Error in GET handler:", err);
const statusCode = error.statusCode || 500; const statusCode = err.statusCode || 500;
// @ts-ignore const message = err.message || "Internal Server Error";
const message = error.message || "Internal Server Error";
return NextResponse.json({ error: message }, { status: statusCode }); return NextResponse.json({ error: message }, { status: statusCode });
} }
}); }

View File

@ -6,8 +6,15 @@ import { NextResponse } from "next/server";
export const dynamic = "force-dynamic"; export const dynamic = "force-dynamic";
export const GET = auth(async function GET(req) { interface ResError extends Error {
if (!req.auth && getEnv("SitePassword")) { statusCode: number;
message: string;
}
export async function GET() {
const session = await auth();
if (!session && getEnv("SitePassword")) {
redirect("/"); redirect("/");
} }
@ -15,11 +22,10 @@ export const GET = auth(async function GET(req) {
const data = await GetNezhaData(); const data = await GetNezhaData();
return NextResponse.json(data, { status: 200 }); return NextResponse.json(data, { status: 200 });
} catch (error) { } catch (error) {
console.error("Error in GET handler:", error); const err = error as ResError;
// @ts-ignore console.error("Error in GET handler:", err);
const statusCode = error.statusCode || 500; const statusCode = err.statusCode || 500;
// @ts-ignore const message = err.message || "Internal Server Error";
const message = error.message || "Internal Server Error";
return NextResponse.json({ error: message }, { status: statusCode }); return NextResponse.json({ error: message }, { status: statusCode });
} }
}); }

BIN
bun.lockb

Binary file not shown.

View File

@ -10,7 +10,7 @@ import {
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import getEnv from "@/lib/env-entry"; import getEnv from "@/lib/env-entry";
import { cn, formatBytes, formatNezhaInfo } from "@/lib/utils"; import { cn, formatBytes, formatNezhaInfo } from "@/lib/utils";
import { useLocale, useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
@ -21,7 +21,7 @@ export default function ServerCard({
}) { }) {
const t = useTranslations("ServerCard"); const t = useTranslations("ServerCard");
const router = useRouter(); 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); formatNezhaInfo(serverInfo);
const showFlag = getEnv("NEXT_PUBLIC_ShowFlag") === "true"; const showFlag = getEnv("NEXT_PUBLIC_ShowFlag") === "true";

View File

@ -3,7 +3,7 @@
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import React, { createRef, useEffect, useRef, useState } from "react"; import React, { createRef, useEffect, useRef } from "react";
export default function Switch({ export default function Switch({
allTag, allTag,
@ -23,7 +23,7 @@ export default function Switch({
if (savedTag && allTag.includes(savedTag)) { if (savedTag && allTag.includes(savedTag)) {
onTagChange(savedTag); onTagChange(savedTag);
} }
}, [allTag]); }, [allTag, onTagChange]);
useEffect(() => { useEffect(() => {
const container = scrollRef.current; const container = scrollRef.current;
@ -53,7 +53,7 @@ export default function Switch({
inline: "center", inline: "center",
}); });
} }
}, [nowTag]); }, [nowTag, allTag]);
return ( return (
<div <div

View File

@ -8,7 +8,7 @@ import * as RechartsPrimitive from "recharts";
const THEMES = { light: "", dark: ".dark" } as const; const THEMES = { light: "", dark: ".dark" } as const;
export type ChartConfig = { export type ChartConfig = {
[k in string]: { [k: string]: {
label?: React.ReactNode; label?: React.ReactNode;
icon?: React.ComponentType; icon?: React.ComponentType;
} & ( } & (
@ -68,7 +68,7 @@ ChartContainer.displayName = "Chart";
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter( const colorConfig = Object.entries(config).filter(
([_, config]) => config.theme || config.color, ([, config]) => config.theme || config.color,
); );
if (!colorConfig.length) { if (!colorConfig.length) {

View File

@ -1,8 +1,7 @@
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import * as React from "react"; import * as React from "react";
export interface InputProps export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>( const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => { ({ className, type, ...props }, ref) => {

View File

@ -6,9 +6,12 @@ import { cookies } from "next/headers";
const COOKIE_NAME = "NEXT_LOCALE"; const COOKIE_NAME = "NEXT_LOCALE";
export async function getUserLocale() { 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) { export async function setUserLocale(locale: string) {
cookies().set(COOKIE_NAME, locale); (await cookies()).set(COOKIE_NAME, locale);
} }

View File

@ -76,9 +76,9 @@ export const nezhaFetcher = async (url: string) => {
if (!res.ok) { if (!res.ok) {
const error = new Error("An error occurred while fetching the data."); const error = new Error("An error occurred while fetching the data.");
// @ts-ignore // @ts-expect-error - res.json() returns a Promise<any>
error.info = await res.json(); error.info = await res.json();
// @ts-ignore // @ts-expect-error - res.status is a number
error.status = res.status; error.status = res.status;
throw error; throw error;
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "nezha-dash", "name": "nezha-dash",
"version": "1.0.0", "version": "1.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev -p 3020", "dev": "next dev -p 3020",
@ -22,46 +22,53 @@
"@radix-ui/react-tooltip": "^1.1.3", "@radix-ui/react-tooltip": "^1.1.3",
"@trivago/prettier-plugin-sort-imports": "^4.3.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"@typescript-eslint/eslint-plugin": "^8.10.0", "@typescript-eslint/eslint-plugin": "^8.12.2",
"caniuse-lite": "^1.0.30001669", "caniuse-lite": "^1.0.30001674",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"country-flag-icons": "^1.5.13", "country-flag-icons": "^1.5.13",
"eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-simple-import-sort": "^12.1.1",
"flag-icons": "^7.2.3", "flag-icons": "^7.2.3",
"framer-motion": "^11.11.9", "framer-motion": "^12.0.0-alpha.1",
"lucide-react": "^0.451.0", "lucide-react": "^0.451.0",
"luxon": "^3.5.0", "luxon": "^3.5.0",
"next": "^14.2.15", "next": "15.0.2",
"next-auth": "^5.0.0-beta.25", "next-auth": "^5.0.0-beta.25",
"next-intl": "^3.21.1", "next-intl": "^3.23.5",
"next-runtime-env": "^3.2.2", "next-runtime-env": "^3.2.2",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react": "^18.3.1", "react": "19.0.0-rc-02c0e824-20241028",
"react-device-detect": "^2.2.3", "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-intersection-observer": "^9.13.1",
"react-wrap-balancer": "^1.1.1", "react-wrap-balancer": "^1.1.1",
"recharts": "2.12.7", "recharts": "2.13.1",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"swr": "^2.2.6-beta.4", "swr": "^2.2.6-beta.4",
"tailwind-merge": "^2.5.4", "tailwind-merge": "^2.5.4",
"tailwindcss-animate": "^1.0.7" "tailwindcss-animate": "^1.0.7",
"typescript-eslint": "^8.12.2"
}, },
"devDependencies": { "devDependencies": {
"eslint-plugin-turbo": "^2.2.1", "eslint-plugin-turbo": "^2.2.3",
"eslint-plugin-unused-imports": "^4.1.4", "eslint-plugin-unused-imports": "^4.1.4",
"@next/bundle-analyzer": "^14.2.15", "@next/bundle-analyzer": "15.0.2",
"@types/node": "^22.7.7", "@types/node": "^22.8.4",
"@types/react": "^18.3.11", "@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "^18.3.1", "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^9.13.0", "eslint": "^9.13.0",
"eslint-config-next": "^14.2.15", "eslint-config-next": "15.0.2",
"postcss": "^8.4.47", "postcss": "^8.4.47",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.8", "prettier-plugin-tailwindcss": "^0.6.8",
"tailwindcss": "^3.4.14", "tailwindcss": "^3.4.14",
"typescript": "^5.6.3" "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"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "HomeDash", "name": "NezhaDash PWA App",
"short_name": "HomeDash PWA App", "short_name": "NezhaDash",
"icons": [ "icons": [
{ {
"src": "/android-chrome-192x192.png", "src": "/android-chrome-192x192.png",