mirror of
https://github.com/hamster1963/nezha-dash.git
synced 2025-04-24 21:10:45 +08:00
refactor: auth page
This commit is contained in:
parent
6bb1a5a5a0
commit
1a46cec662
@ -63,7 +63,7 @@ function Header() {
|
||||
|
||||
// https://github.com/streamich/react-use/blob/master/src/useInterval.ts
|
||||
const useInterval = (callback: Function, delay?: number | null) => {
|
||||
const savedCallback = useRef<Function>(() => { });
|
||||
const savedCallback = useRef<Function>(() => {});
|
||||
useEffect(() => {
|
||||
savedCallback.current = callback;
|
||||
});
|
||||
@ -98,7 +98,9 @@ function Overview() {
|
||||
</p>
|
||||
{mouted ? (
|
||||
<p className="opacity-1 text-sm font-medium">{timeString}</p>
|
||||
) : <Skeleton className="h-[20px] w-[50px] rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>}
|
||||
) : (
|
||||
<Skeleton className="h-[20px] w-[50px] rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
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 { SignIn } from "@/components/SignIn";
|
||||
import getEnv from "@/lib/env-entry";
|
||||
import { redirect } from "next/navigation";
|
||||
import React from "react";
|
||||
@ -13,11 +13,7 @@ export default async function MainLayout({ children }: DashboardProps) {
|
||||
const session = await auth();
|
||||
|
||||
if (!session && getEnv("SitePassword")) {
|
||||
if (getEnv("CF_PAGES")) {
|
||||
redirect("/api/auth/signin");
|
||||
} else {
|
||||
return <SignIn />;
|
||||
}
|
||||
return <SignIn />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
9
auth.ts
9
auth.ts
@ -6,14 +6,15 @@ import getEnv from "./lib/env-entry";
|
||||
export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
secret: "this_is_nezha_dash_web_secret",
|
||||
trustHost: true,
|
||||
pages: {
|
||||
signIn: "/",
|
||||
},
|
||||
providers: [
|
||||
Credentials({
|
||||
credentials: {
|
||||
password: {},
|
||||
},
|
||||
credentials: { password: { label: "Password", type: "password" } },
|
||||
authorize: async (credentials) => {
|
||||
if (credentials.password === getEnv("SitePassword")) {
|
||||
return { id: "0" };
|
||||
return { id: "nezha-dash-auth" };
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
66
components/SignIn.tsx
Normal file
66
components/SignIn.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
"use client";
|
||||
|
||||
import Footer from "@/app/[locale]/(main)/footer";
|
||||
import Header from "@/app/[locale]/(main)/header";
|
||||
import { getCsrfToken } from "next-auth/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function SignIn({}) {
|
||||
const t = useTranslations("SignIn");
|
||||
|
||||
const [csrfToken, setCsrfToken] = useState("");
|
||||
const [errorState, setErrorState] = useState(false);
|
||||
|
||||
const search = useSearchParams();
|
||||
const error = search.get("error");
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
setErrorState(true);
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadProviders() {
|
||||
const csrf = await getCsrfToken();
|
||||
setCsrfToken(csrf);
|
||||
}
|
||||
loadProviders();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen w-full flex-col">
|
||||
<main className="flex min-h-[calc(100vh_-_theme(spacing.16))] flex-1 flex-col gap-4 bg-muted/40 p-4 md:p-10 md:pt-8">
|
||||
<Header />
|
||||
<form
|
||||
className="flex flex-col items-center justify-start gap-4 p-4 "
|
||||
method="post"
|
||||
action="/api/auth/callback/credentials"
|
||||
>
|
||||
<input type="hidden" name="csrfToken" value={csrfToken} />
|
||||
<section className="flex flex-col items-start gap-2">
|
||||
<label className="flex flex-col items-start gap-1 ">
|
||||
{errorState && (
|
||||
<p className="text-red-500 text-sm font-semibold">
|
||||
{t("ErrorMessage")}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-base font-semibold">{t("SignInMessage")}</p>
|
||||
<input
|
||||
className="px-1 border-[1px] rounded-[5px]"
|
||||
name="password"
|
||||
type="password"
|
||||
/>
|
||||
</label>
|
||||
<button className=" px-1.5 py-0.5 w-fit text-sm font-semibold rounded-[8px] border bg-card hover:brightness-95 transition-all text-card-foreground shadow-lg shadow-neutral-200/40 dark:shadow-none">
|
||||
{t("Submit")}
|
||||
</button>
|
||||
</section>
|
||||
</form>
|
||||
<Footer />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
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 (
|
||||
<div className="flex min-h-screen w-full flex-col">
|
||||
<main className="flex min-h-[calc(100vh_-_theme(spacing.16))] flex-1 flex-col gap-4 bg-muted/40 p-4 md:p-10 md:pt-8">
|
||||
<Header />
|
||||
<form
|
||||
className="flex flex-col items-center justify-start gap-4 p-4 "
|
||||
action={handleSubmit}
|
||||
>
|
||||
<section className="flex flex-col items-start gap-2">
|
||||
<label className="flex flex-col items-start gap-1 ">
|
||||
<p className="text-base font-semibold">请输入页面密码</p>
|
||||
<input
|
||||
className="px-1 border-[1px] rounded-[5px]"
|
||||
name="password"
|
||||
type="password"
|
||||
/>
|
||||
</label>
|
||||
<button className=" px-1.5 py-0.5 w-fit text-sm font-semibold rounded-[8px] border bg-card hover:brightness-95 transition-all text-card-foreground shadow-lg shadow-neutral-200/40 dark:shadow-none">
|
||||
确认
|
||||
</button>
|
||||
</section>
|
||||
</form>
|
||||
<Footer />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -23,6 +23,11 @@
|
||||
"Detail": "Detail",
|
||||
"Network": "Network"
|
||||
},
|
||||
"SignIn": {
|
||||
"SignInMessage": "Please enter the password",
|
||||
"Submit": "Login",
|
||||
"ErrorMessage": "Invalid password"
|
||||
},
|
||||
"ServerCardPopover": {
|
||||
"System": "System",
|
||||
"CPU": "CPU",
|
||||
|
@ -23,6 +23,11 @@
|
||||
"Detail": "詳細",
|
||||
"Network": "ネットワーク"
|
||||
},
|
||||
"SignIn": {
|
||||
"SignInMessage": "パスワードを入力してください",
|
||||
"Submit": "ログイン",
|
||||
"ErrorMessage": "パスワードが間違っています"
|
||||
},
|
||||
"ServerCardPopover": {
|
||||
"System": "システム",
|
||||
"CPU": "CPU",
|
||||
|
@ -23,6 +23,11 @@
|
||||
"Detail": "詳細",
|
||||
"Network": "網路"
|
||||
},
|
||||
"SignIn": {
|
||||
"SignInMessage": "請輸入密碼",
|
||||
"Submit": "登入",
|
||||
"ErrorMessage": "密碼錯誤"
|
||||
},
|
||||
"ServerCardPopover": {
|
||||
"System": "系統",
|
||||
"CPU": "CPU",
|
||||
|
@ -23,6 +23,11 @@
|
||||
"Detail": "详情",
|
||||
"Network": "网络"
|
||||
},
|
||||
"SignIn": {
|
||||
"SignInMessage": "请输入密码",
|
||||
"Submit": "登录",
|
||||
"ErrorMessage": "密码错误"
|
||||
},
|
||||
"ServerCardPopover": {
|
||||
"System": "系统",
|
||||
"CPU": "CPU",
|
||||
|
@ -23,11 +23,6 @@ const withPWA = withPWAInit({
|
||||
const nextConfig = {
|
||||
output: "standalone",
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
serverActions: {
|
||||
allowedOrigins: ["*"],
|
||||
},
|
||||
},
|
||||
logging: {
|
||||
fetches: {
|
||||
fullUrl: true,
|
||||
|
Loading…
Reference in New Issue
Block a user