feat: polish lite auth entry points

This commit is contained in:
JetSprow
2026-04-30 15:04:21 +10:00
parent b2deee608f
commit 85abba9bbf
10 changed files with 54 additions and 27 deletions

View File

@@ -3,6 +3,7 @@ import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { PublicNotice } from "../public-notice";
import { SiteFooter } from "@/components/shared/site-footer";
import { PRODUCT_EDITION } from "@/lib/product";
export function AuthShell({ children }: { children: ReactNode }) {
return (
@@ -36,7 +37,7 @@ export function AuthCard({
{(title || description) && (
<CardHeader className="space-y-2 pt-6 text-center">
<div className="mx-auto flex size-10 items-center justify-center rounded-lg bg-primary text-sm font-bold text-primary-foreground">
S
{PRODUCT_EDITION.slice(0, 1)}
</div>
{title && <h1 className="text-display text-2xl font-semibold">{title}</h1>}
{description && <p className="text-sm leading-6 text-muted-foreground">{description}</p>}

View File

@@ -3,7 +3,7 @@ import { ForgotPasswordClient } from "./forgot-password-client";
export const metadata: Metadata = {
title: "找回密码",
description: "通过邮箱重设 J-Board 账户密码。",
description: "通过邮箱重设 J-Board Lite 账户密码。",
};
export default function ForgotPasswordPage() {

View File

@@ -7,10 +7,10 @@ import { PageTransition } from "@/components/shared/page-transition";
export const metadata: Metadata = {
title: {
default: "登录与注册",
template: "%s | J-Board",
default: "账户入口",
template: "%s | J-Board Lite",
},
description: "登录或注册 J-Board 账号。",
description: "登录 J-Board Lite 账号,或通过邮箱找回密码。",
};
export default async function AuthLayout({

View File

@@ -9,9 +9,16 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { TurnstileWidget } from "@/components/shared/turnstile-widget";
import { PRODUCT_NAME } from "@/lib/product";
import { AuthCard, AuthErrorMessage, AuthShell } from "../_components/auth-shell";
export function LoginPageClient({ siteKey }: { siteKey?: string | null }) {
export function LoginPageClient({
siteKey,
allowRegistration,
}: {
siteKey?: string | null;
allowRegistration: boolean;
}) {
const router = useRouter();
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
@@ -43,7 +50,7 @@ export function LoginPageClient({ siteKey }: { siteKey?: string | null }) {
return (
<AuthShell>
<AuthCard title="J-Board" description="登录你的 JB面板账户">
<AuthCard title={PRODUCT_NAME} description="登录你的面板账户">
<form onSubmit={onSubmit} className="space-y-4">
<AuthErrorMessage message={error} />
<div className="space-y-2">
@@ -63,6 +70,8 @@ export function LoginPageClient({ siteKey }: { siteKey?: string | null }) {
<Link href="/forgot-password" className="font-medium text-primary hover:underline">
</Link>
{allowRegistration && (
<>
<span className="h-1 w-1 rounded-full bg-muted-foreground/30" aria-hidden />
<span>
{" "}
@@ -70,6 +79,8 @@ export function LoginPageClient({ siteKey }: { siteKey?: string | null }) {
</Link>
</span>
</>
)}
{error === "邮箱尚未验证,请先查收验证邮件" && (
<>
<span className="h-1 w-1 rounded-full bg-muted-foreground/30" aria-hidden />

View File

@@ -4,10 +4,10 @@ import { LoginPageClient } from "./login-page-client";
export const metadata: Metadata = {
title: "登录",
description: "登录 J-Board 账户并进入用户中心。",
description: "登录 J-Board Lite 账户并进入用户中心。",
};
export default async function LoginPage() {
const config = await getAppConfig();
return <LoginPageClient siteKey={config.turnstileSiteKey} />;
return <LoginPageClient siteKey={config.turnstileSiteKey} allowRegistration={config.allowRegistration} />;
}

View File

@@ -1,13 +1,15 @@
import type { Metadata } from "next";
import { redirect } from "next/navigation";
import { getAppConfig } from "@/services/app-config";
import { RegisterPageClient } from "./register-page-client";
export const metadata: Metadata = {
title: "注册",
description: "创建 J-Board 新账户并开始订阅服务。",
description: "创建 J-Board Lite 新账户并开始订阅服务。",
};
export default async function RegisterPage() {
const config = await getAppConfig();
if (!config.allowRegistration) redirect("/login");
return <RegisterPageClient siteKey={config.turnstileSiteKey} />;
}

View File

@@ -8,6 +8,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { TurnstileWidget } from "@/components/shared/turnstile-widget";
import { getErrorMessage } from "@/lib/errors";
import { PRODUCT_NAME } from "@/lib/product";
import { AuthCard, AuthErrorMessage, AuthShell } from "../_components/auth-shell";
export function RegisterPageClient({ siteKey }: { siteKey?: string | null }) {
@@ -80,7 +81,7 @@ export function RegisterPageClient({ siteKey }: { siteKey?: string | null }) {
return (
<AuthShell>
<AuthCard title="J-Board" description="创建 JB面板账户">
<AuthCard title={PRODUCT_NAME} description="创建面板账户">
<form onSubmit={onSubmit} className="space-y-4">
<AuthErrorMessage message={error} />
<div className="space-y-2">

View File

@@ -1,14 +1,15 @@
import type { Metadata } from "next";
import { Toaster } from "@/components/ui/sonner";
import { ThemeProvider } from "@/components/shared/theme-provider";
import { PRODUCT_NAME } from "@/lib/product";
import "./globals.css";
export const metadata: Metadata = {
title: {
default: "J-Board - JB面板",
template: "%s | J-Board",
default: `${PRODUCT_NAME} - JB面板轻量版`,
template: `%s | ${PRODUCT_NAME}`,
},
description: "J-BoardJB面板订阅共享与节点管理平台",
description: "J-Board LiteJB面板轻量版)订阅共享与节点管理平台",
};
export default function RootLayout({

View File

@@ -1,23 +1,28 @@
import { GitFork } from "lucide-react";
import { cn } from "@/lib/utils";
import { ThemeToggle } from "./theme-toggle";
const GITHUB_URL = "https://github.com/JetSprow/J-Board";
import { PRODUCT_EDITION, PRODUCT_NAME, PRODUCT_REPOSITORY_URL, PRODUCT_VERSION } from "@/lib/product";
export function SiteFooter({ className }: { className?: string }) {
return (
<footer
className={cn(
"mx-auto flex w-full max-w-md items-center justify-center gap-2 text-xs text-muted-foreground/70",
"mx-auto flex w-full max-w-md flex-wrap items-center justify-center gap-2 text-xs text-muted-foreground/70",
className,
)}
>
<span>J-Board</span>
<span className="h-1 w-1 rounded-full bg-muted-foreground/30" aria-hidden />
<div className="inline-flex min-h-8 items-center gap-2 rounded-lg border border-border/70 bg-card/80 px-2.5 py-1 text-foreground shadow-sm">
<span className="text-muted-foreground"></span>
<span className="font-semibold">{PRODUCT_NAME}</span>
<span className="rounded-md bg-primary/10 px-1.5 py-0.5 text-[0.68rem] font-semibold uppercase text-primary">
{PRODUCT_EDITION}
</span>
<span className="font-mono text-[0.72rem] text-muted-foreground">v{PRODUCT_VERSION}</span>
</div>
<ThemeToggle className="size-7 rounded-md border-transparent bg-transparent" />
<span className="h-1 w-1 rounded-full bg-muted-foreground/30" aria-hidden />
<a
href={GITHUB_URL}
href={PRODUCT_REPOSITORY_URL}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-1.5 rounded-md px-1.5 py-1 font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/15"

6
src/lib/product.ts Normal file
View File

@@ -0,0 +1,6 @@
import packageJson from "../../package.json";
export const PRODUCT_NAME = "J-Board Lite";
export const PRODUCT_EDITION = "Lite";
export const PRODUCT_VERSION = packageJson.version;
export const PRODUCT_REPOSITORY_URL = "https://github.com/JetSprow/J-Board";