diff --git a/app/[locale]/(main)/header.tsx b/app/[locale]/(main)/header.tsx index 08944ee..a7d9ec7 100644 --- a/app/[locale]/(main)/header.tsx +++ b/app/[locale]/(main)/header.tsx @@ -20,6 +20,7 @@ function Header() { const router = useRouter(); const locale = useLocale(); + return (
diff --git a/app/[locale]/(main)/layout.tsx b/app/[locale]/(main)/layout.tsx index 84bd06e..82a15af 100644 --- a/app/[locale]/(main)/layout.tsx +++ b/app/[locale]/(main)/layout.tsx @@ -1,11 +1,17 @@ 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 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")) return + return (
diff --git a/app/[locale]/(main)/page.tsx b/app/[locale]/(main)/page.tsx index f40711c..988a736 100644 --- a/app/[locale]/(main)/page.tsx +++ b/app/[locale]/(main)/page.tsx @@ -10,6 +10,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..2b2864f 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -13,6 +13,7 @@ import { Inter as FontSans } from "next/font/google"; import React from "react"; import "/node_modules/flag-icons/css/flag-icons.min.css"; +import { auth } from "@/auth"; const fontSans = FontSans({ subsets: ["latin"], @@ -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..0cf1408 --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,2 @@ +import { handlers } from "@/auth" // Referring to the auth.ts we just created +export const { GET, POST } = handlers \ No newline at end of file diff --git a/app/api/detail/route.ts b/app/api/detail/route.ts index 4fd0885..81538db 100644 --- a/app/api/detail/route.ts +++ b/app/api/detail/route.ts @@ -1,6 +1,8 @@ import { NezhaAPISafe } from "@/app/[locale]/types/nezha-api"; import { GetServerDetail } from "@/lib/serverFetch"; import { NextResponse } from "next/server"; +import { auth } from "@/auth" +import getEnv from "@/lib/env-entry"; export const dynamic = "force-dynamic"; export const runtime = 'edge'; @@ -10,7 +12,12 @@ 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) { @@ -27,4 +34,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 bc10201..0f44513 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"; @@ -10,7 +12,12 @@ 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) { @@ -27,4 +34,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 d3cf464..41db0d5 100644 --- a/app/api/server/route.ts +++ b/app/api/server/route.ts @@ -1,5 +1,6 @@ import { NezhaAPI, ServerApi } from "@/app/[locale]/types/nezha-api"; +import { auth } from "@/auth"; import { MakeOptional } from "@/app/[locale]/types/utils"; import getEnv from "@/lib/env-entry"; import { NextResponse } from "next/server"; @@ -13,14 +14,19 @@ 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 }); -} +}); async function GetNezhaData() { diff --git a/auth.ts b/auth.ts new file mode 100644 index 0000000..a908388 --- /dev/null +++ b/auth.ts @@ -0,0 +1,20 @@ +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 + }, + }), + ], +}) \ No newline at end of file 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..8d266c5 --- /dev/null +++ b/components/sign-in.tsx @@ -0,0 +1,40 @@ +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..5713407 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",