diff --git a/app/[locale]/(main)/layout.tsx b/app/[locale]/(main)/layout.tsx index 84bd06e..ef107f1 100644 --- a/app/[locale]/(main)/layout.tsx +++ b/app/[locale]/(main)/layout.tsx @@ -1,11 +1,25 @@ import Footer from "@/app/[locale]/(main)/footer"; import Header from "@/app/[locale]/(main)/header"; +import { auth } from "@/auth"; +import { SignIn } from "@/components/sign-in"; +import getEnv from "@/lib/env-entry"; +import { redirect } from "next/navigation"; import React from "react"; type DashboardProps = { children: React.ReactNode; }; -export default function MainLayout({ children }: DashboardProps) { +export default async function MainLayout({ children }: DashboardProps) { + const session = await auth(); + + if (!session && getEnv("SITE_PASSWORD")) { + if (getEnv("CF_PAGES")) { + redirect("/api/auth/signin"); + } else { + return ; + } + } + return (
diff --git a/app/[locale]/(main)/page.tsx b/app/[locale]/(main)/page.tsx index 79b4bd9..0e568e6 100644 --- a/app/[locale]/(main)/page.tsx +++ b/app/[locale]/(main)/page.tsx @@ -8,6 +8,7 @@ export default function Home({ params: { locale: string }; }) { unstable_setRequestLocale(locale); + return (
diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 4671348..9e6ce07 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -1,4 +1,5 @@ // @auto-i18n-check. Please do not delete the line. +import { auth } from "@/auth"; import { locales } from "@/i18n-metadata"; import getEnv from "@/lib/env-entry"; import { cn } from "@/lib/utils"; @@ -54,6 +55,7 @@ export default function LocaleLayout({ unstable_setRequestLocale(locale); const messages = useMessages(); + return ( diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..87f6488 --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,4 @@ +import { handlers } from "@/auth"; + +// Referring to the auth.ts we just created +export const { GET, POST } = handlers; diff --git a/app/api/detail/route.ts b/app/api/detail/route.ts index b0f8bc1..17ef913 100644 --- a/app/api/detail/route.ts +++ b/app/api/detail/route.ts @@ -1,4 +1,6 @@ import { NezhaAPISafe } from "@/app/[locale]/types/nezha-api"; +import { auth } from "@/auth"; +import getEnv from "@/lib/env-entry"; import { GetServerDetail } from "@/lib/serverFetch"; import { NextResponse } from "next/server"; @@ -9,7 +11,11 @@ interface NezhaDataResponse { data?: NezhaAPISafe; } -export async function GET(req: Request) { +export const GET = auth(async function GET(req) { + if (!req.auth && getEnv("SITE_PASSWORD")) { + return NextResponse.json({ message: "Not authenticated" }, { status: 401 }); + } + const { searchParams } = new URL(req.url); const server_id = searchParams.get("server_id"); if (!server_id) { @@ -26,4 +32,4 @@ export async function GET(req: Request) { return NextResponse.json({ error: response.error }, { status: 400 }); } return NextResponse.json(response, { status: 200 }); -} +}); diff --git a/app/api/monitor/route.ts b/app/api/monitor/route.ts index e03fe89..528de95 100644 --- a/app/api/monitor/route.ts +++ b/app/api/monitor/route.ts @@ -1,4 +1,6 @@ import { ServerMonitorChart } from "@/app/[locale]/types/nezha-api"; +import { auth } from "@/auth"; +import getEnv from "@/lib/env-entry"; import { GetServerMonitor } from "@/lib/serverFetch"; import { NextResponse } from "next/server"; @@ -9,7 +11,11 @@ interface NezhaDataResponse { data?: ServerMonitorChart; } -export async function GET(req: Request) { +export const GET = auth(async function GET(req) { + if (!req.auth && getEnv("SITE_PASSWORD")) { + return NextResponse.json({ message: "Not authenticated" }, { status: 401 }); + } + const { searchParams } = new URL(req.url); const server_id = searchParams.get("server_id"); if (!server_id) { @@ -26,4 +32,4 @@ export async function GET(req: Request) { return NextResponse.json({ error: response.error }, { status: 400 }); } return NextResponse.json(response, { status: 200 }); -} +}); diff --git a/app/api/server/route.ts b/app/api/server/route.ts index e34a83c..f201247 100644 --- a/app/api/server/route.ts +++ b/app/api/server/route.ts @@ -1,4 +1,6 @@ import { ServerApi } from "@/app/[locale]/types/nezha-api"; +import { auth } from "@/auth"; +import getEnv from "@/lib/env-entry"; import { GetNezhaData } from "@/lib/serverFetch"; import { NextResponse } from "next/server"; @@ -9,11 +11,15 @@ interface NezhaDataResponse { data?: ServerApi; } -export async function GET(_: Request) { +export const GET = auth(async function GET(req) { + if (!req.auth && getEnv("SITE_PASSWORD")) { + return NextResponse.json({ message: "Not authenticated" }, { status: 401 }); + } + const response = (await GetNezhaData()) as NezhaDataResponse; if (response.error) { console.log(response.error); return NextResponse.json({ error: response.error }, { status: 400 }); } return NextResponse.json(response, { status: 200 }); -} +}); diff --git a/auth.ts b/auth.ts new file mode 100644 index 0000000..ca98657 --- /dev/null +++ b/auth.ts @@ -0,0 +1,21 @@ +import NextAuth from "next-auth"; +import Credentials from "next-auth/providers/credentials"; + +import getEnv from "./lib/env-entry"; + +export const { handlers, signIn, signOut, auth } = NextAuth({ + secret: "this_is_nezha_dash_web_secret", + providers: [ + Credentials({ + credentials: { + password: {}, + }, + authorize: async (credentials) => { + if (credentials.password === getEnv("SITE_PASSWORD")) { + return { id: "0" }; + } + return null; + }, + }), + ], +}); diff --git a/bun.lockb b/bun.lockb index e99d417..a0f7e64 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/sign-in.tsx b/components/sign-in.tsx new file mode 100644 index 0000000..c8adbea --- /dev/null +++ b/components/sign-in.tsx @@ -0,0 +1,45 @@ +import Footer from "@/app/[locale]/(main)/footer"; +import Header from "@/app/[locale]/(main)/header"; +import { signIn } from "@/auth"; +import { useLocale } from "next-intl"; +import { redirect } from "next/navigation"; + +export function SignIn() { + const locale = useLocale(); + + async function handleSubmit(formData: FormData) { + "use server"; + try { + await signIn("credentials", formData); + } catch (error) { + redirect(`/${locale}`); + } + } + + return ( +
+
+
+
+
+ + +
+
+
+
+ ); +} diff --git a/middleware.ts b/middleware.ts index 92342ad..fa0386d 100644 --- a/middleware.ts +++ b/middleware.ts @@ -3,6 +3,8 @@ import createMiddleware from "next-intl/middleware"; import { defaultLocale, locales } from "./i18n-metadata"; +// export { auth as middleware } from "@/auth" + export default createMiddleware({ // A list of all locales that are supported locales: locales, diff --git a/next.config.mjs b/next.config.mjs index 86bfbf5..a68be47 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -23,6 +23,11 @@ const withPWA = withPWAInit({ const nextConfig = { output: "standalone", reactStrictMode: true, + experimental: { + serverActions: { + allowedOrigins: ["*"], + }, + }, logging: { fetches: { fullUrl: true, diff --git a/package.json b/package.json index e94e7b2..76c090f 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "lucide-react": "^0.451.0", "luxon": "^3.5.0", "next": "^14.2.15", + "next-auth": "^5.0.0-beta.25", "next-intl": "^3.21.1", "next-runtime-env": "^3.2.2", "next-themes": "^0.3.0",